1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use crate::{AsClasses, HelperText, Icon, ValidationContext};
use yew::{Callback, Classes};

#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum InputState {
    Default,
    Success,
    Warning,
    Error,
}

impl Default for InputState {
    fn default() -> Self {
        Self::Default
    }
}

impl AsClasses for InputState {
    fn extend(&self, classes: &mut Classes) {
        match self {
            Self::Default => {}
            Self::Success => classes.push("pf-m-success"),
            Self::Warning => classes.push("pf-m-warning"),
            Self::Error => classes.push("pf-m-error"),
        }
    }
}

impl InputState {
    pub fn convert(&self, mut classes: Classes) -> (Classes, bool) {
        let mut aria_invalid = false;
        match self {
            InputState::Default => {}
            InputState::Success => classes.push("pf-m-success"),
            InputState::Warning => classes.push("pf-m-warning"),
            InputState::Error => aria_invalid = true,
        };
        (classes, aria_invalid)
    }

    pub fn icon(&self) -> Icon {
        match self {
            Self::Default => Icon::Minus,
            Self::Success => Icon::CheckCircle,
            Self::Warning => Icon::ExclamationTriangle,
            Self::Error => Icon::ExclamationCircle,
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ValidationResult {
    pub message: Option<String>,
    pub state: InputState,
}

impl From<ValidationResult> for Option<HelperText> {
    fn from(result: ValidationResult) -> Self {
        if matches!(result.state, InputState::Default) && result.message.is_none() {
            // default state and no message
            None
        } else {
            // non-default state or some message
            Some(HelperText {
                message: result.message.unwrap_or_default(),
                input_state: result.state,
                custom_icon: None,
                no_icon: match result.state {
                    InputState::Default => true,
                    _ => false,
                },
                is_dynamic: true,
            })
        }
    }
}

impl Default for ValidationResult {
    fn default() -> Self {
        ValidationResult::ok()
    }
}

impl ValidationResult {
    pub fn ok() -> Self {
        Self {
            message: None,
            state: Default::default(),
        }
    }

    pub fn new<S: Into<String>>(state: InputState, message: S) -> Self {
        Self {
            state,
            message: Some(message.into()),
        }
    }

    pub fn help<S: Into<String>>(message: S) -> Self {
        Self::new(InputState::Default, message)
    }

    pub fn error<S: Into<String>>(message: S) -> Self {
        Self::new(InputState::Error, message)
    }

    pub fn warning<S: Into<String>>(message: S) -> Self {
        Self::new(InputState::Warning, message)
    }
}

pub trait ValidatingComponent {
    type Value;
}

pub trait ValidatingComponentProperties<T> {
    fn set_onvalidate(&mut self, onvalidate: Callback<ValidationContext<T>>);
    fn set_input_state(&mut self, state: InputState);
}