1use crate::Signal;
6use std::{fmt::Display, str::FromStr};
7
8#[diagnostic::on_unimplemented(
14 message = "`bind` on this control needs a `Signal<T>` where `T: FromStr + Display + PartialEq`, or a form field",
15 label = "`{Self}` cannot be edited as text"
16)]
17pub trait TextValue: Clone + 'static {
18 fn text(&self) -> String;
20 fn shows(&self, text: &str) -> bool;
23 fn edit(&self, text: String);
25 fn touch(&self) {}
27}
28
29impl<T: FromStr + Display + PartialEq + 'static> TextValue for Signal<T> {
30 fn text(&self) -> String {
31 self.with(ToString::to_string)
32 }
33 fn shows(&self, text: &str) -> bool {
34 text.parse::<T>()
35 .is_ok_and(|parsed| self.with(|value| *value == parsed))
36 }
37 fn edit(&self, text: String) {
38 if let Ok(value) = text.parse::<T>() {
39 self.set(value);
40 }
41 }
42}
43
44#[diagnostic::on_unimplemented(
47 message = "`bind` on a checkbox needs a `Signal<bool>` or a `Signal<Vec<T>>`",
48 label = "`{Self}` cannot follow a checkbox"
49)]
50pub trait Checkbox: Clone + 'static {
51 fn checked(&self, choice: &str) -> bool;
53 fn check(&self, choice: &str, checked: bool);
55}
56
57impl Checkbox for Signal<bool> {
58 fn checked(&self, _: &str) -> bool {
59 self.get()
60 }
61 fn check(&self, _: &str, checked: bool) {
62 self.set(checked);
63 }
64}
65
66impl<T: FromStr + PartialEq + 'static> Checkbox for Signal<Vec<T>> {
67 fn checked(&self, choice: &str) -> bool {
68 selected(self, choice)
69 }
70 fn check(&self, choice: &str, checked: bool) {
71 let Ok(choice) = choice.parse::<T>() else {
72 return;
73 };
74 if self.with_untracked(|values| values.contains(&choice)) != checked {
75 self.update(|values| {
76 if checked {
77 values.push(choice);
78 } else {
79 values.retain(|value| *value != choice);
80 }
81 });
82 }
83 }
84}
85
86pub fn selected<T: FromStr + PartialEq>(values: &Signal<Vec<T>>, choice: &str) -> bool {
89 choice
90 .parse::<T>()
91 .is_ok_and(|choice| values.with(|values| values.contains(&choice)))
92}