patternfly_yew/components/form/
validation.rs

1use crate::prelude::{AsClasses, FormHelperText, Icon, ValidationContext};
2use yew::{Callback, Classes};
3
4/// State of an input from validation
5#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
6pub enum InputState {
7    Default,
8    Success,
9    Warning,
10    Error,
11}
12
13impl Default for InputState {
14    fn default() -> Self {
15        Self::Default
16    }
17}
18
19impl AsClasses for InputState {
20    fn extend_classes(&self, classes: &mut Classes) {
21        match self {
22            Self::Default => {}
23            Self::Success => classes.push("pf-m-success"),
24            Self::Warning => classes.push("pf-m-warning"),
25            Self::Error => classes.push("pf-m-error"),
26        }
27    }
28}
29
30impl InputState {
31    pub fn convert(&self, mut classes: Classes) -> (Classes, bool) {
32        let mut aria_invalid = false;
33        match self {
34            InputState::Default => {}
35            InputState::Success => classes.push("pf-m-success"),
36            InputState::Warning => classes.push("pf-m-warning"),
37            InputState::Error => {
38                classes.push("pf-m-error");
39                aria_invalid = true;
40            }
41        };
42        (classes, aria_invalid)
43    }
44
45    pub fn icon(&self) -> Icon {
46        match self {
47            Self::Default => Icon::Minus,
48            Self::Success => Icon::CheckCircle,
49            Self::Warning => Icon::ExclamationTriangle,
50            Self::Error => Icon::ExclamationCircle,
51        }
52    }
53}
54
55/// Result of the validation
56#[derive(Clone, Debug, PartialEq, Eq)]
57pub struct ValidationResult {
58    pub message: Option<String>,
59    pub state: InputState,
60}
61
62impl From<ValidationResult> for Option<FormHelperText> {
63    fn from(result: ValidationResult) -> Self {
64        if matches!(result.state, InputState::Default) || result.message.is_none() {
65            // default state and no message
66            None
67        } else {
68            // non-default state or some message
69            Some(FormHelperText {
70                message: result.message.unwrap_or_default(),
71                input_state: result.state,
72                custom_icon: None,
73                no_icon: matches!(result.state, InputState::Default),
74                is_dynamic: true,
75            })
76        }
77    }
78}
79
80impl Default for ValidationResult {
81    fn default() -> Self {
82        ValidationResult::ok()
83    }
84}
85
86impl ValidationResult {
87    pub fn ok() -> Self {
88        Self {
89            message: None,
90            state: Default::default(),
91        }
92    }
93
94    pub fn new<S: Into<String>>(state: InputState, message: S) -> Self {
95        Self {
96            state,
97            message: Some(message.into()),
98        }
99    }
100
101    pub fn help<S: Into<String>>(message: S) -> Self {
102        Self::new(InputState::Default, message)
103    }
104
105    pub fn error<S: Into<String>>(message: S) -> Self {
106        Self::new(InputState::Error, message)
107    }
108
109    pub fn warning<S: Into<String>>(message: S) -> Self {
110        Self::new(InputState::Warning, message)
111    }
112}
113
114/// A component supporting validation.
115pub trait ValidatingComponent {
116    type Value;
117}
118
119/// A trait which components supporting validatio must implement.
120pub trait ValidatingComponentProperties<T> {
121    fn set_onvalidate(&mut self, onvalidate: Callback<ValidationContext<T>>);
122    fn set_input_state(&mut self, state: InputState);
123}