Skip to main content

jinya_ui/widgets/form/
radio.rs

1use yew::prelude::*;
2use yew::{Callback, Component, ComponentLink, Html};
3
4pub fn get_css<'a>() -> &'a str {
5    // language=CSS
6    "
7.jinya-radio__color-container {
8    flex: 0 0 100%;
9}
10
11.jinya-radio__color-container--default {
12    --state-color: var(--primary-color);
13}
14
15.jinya-radio__color-container--negative {
16    --state-color: var(--negative-color);
17}
18
19.jinya-radio__color-container--positive {
20    --state-color: var(--positive-color);
21}
22
23.jinya-radio__color-container--disabled {
24    --state-color: var(--disabled-border-color);
25}
26
27.jinya-radio__input {
28    visibility: hidden;
29    position: absolute;
30}
31
32.jinya-radio__input:invalid {
33    outline: none;
34    box-shadow: none;
35    border: none;
36}
37
38.jinya-radio__label {
39    display: block;
40    font-size: var(--font-size-16);
41    color: var(--state-color);
42    background: var(--white);
43    padding-left: 0.25rem;
44    padding-right: 0.25rem;
45    box-sizing: border-box;
46    position: relative;
47}
48
49.jinya-radio__label::before,
50.jinya-radio__label::after {
51    content: '';
52    display: none;
53}
54
55.jinya-radio__label::before {
56    width: 1rem;
57    height: 1rem;
58    display: inline-block;
59    border: 2px solid var(--state-color);
60    border-radius: 50%;
61    box-sizing: border-box;
62    margin-right: 0.5rem;
63    background: var(--white);
64}
65
66.jinya-radio__label:hover::before {
67    background: var(--input-background-color);
68}
69
70.jinya-radio__label::after {
71    position: absolute;
72    top: 5px;
73    left: 7px;
74    height: 10px;
75    width: 10px;
76    border-radius: 50%;
77    transform: rotate(45deg);
78    background: var(--state-color);
79}
80
81.jinya-radio__input:checked + .jinya-radio__label::after {
82    display: block;
83}
84
85.jinya-radio__validation-message {
86    display: block;
87    font-size: var(--font-size-12);
88    color: var(--state-color);
89}
90"
91}
92
93#[derive(Clone, PartialEq)]
94pub enum RadioState {
95    Default,
96    Negative,
97    Positive,
98}
99
100pub struct Radio {
101    link: ComponentLink<Self>,
102    label: String,
103    on_change: Callback<()>,
104    state: RadioState,
105    validation_message: String,
106    checked: bool,
107    group: String,
108    value: String,
109    disabled: bool,
110}
111
112#[derive(Clone, PartialEq, Properties)]
113pub struct RadioProps {
114    pub label: String,
115    pub on_change: Callback<()>,
116    #[prop_or(RadioState::Default)]
117    pub state: RadioState,
118    #[prop_or("".to_string())]
119    pub validation_message: String,
120    pub checked: bool,
121    pub group: String,
122    pub value: String,
123    #[prop_or(false)]
124    pub disabled: bool,
125}
126
127pub enum Msg {
128    Change,
129}
130
131impl Default for RadioState {
132    fn default() -> Self {
133        RadioState::Default
134    }
135}
136
137impl Radio {
138    fn get_radio_container_class(&self) -> String {
139        let class = match self.state {
140            RadioState::Default => {
141                "jinya-radio__color-container jinya-radio__color-container--default"
142            }
143            RadioState::Negative => {
144                "jinya-radio__color-container jinya-radio__color-container--negative"
145            }
146            RadioState::Positive => {
147                "jinya-radio__color-container jinya-radio__color-container--positive"
148            }
149        }
150        .to_string();
151
152        if self.disabled {
153            "jinya-radio__color-container jinya-radio__color-container--disabled".to_string()
154        } else {
155            class
156        }
157    }
158}
159
160impl Component for Radio {
161    type Message = Msg;
162    type Properties = RadioProps;
163
164    fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
165        Radio {
166            link,
167            label: props.label,
168            state: props.state,
169            validation_message: props.validation_message,
170            on_change: props.on_change,
171            checked: props.checked,
172            group: props.group,
173            value: props.value,
174            disabled: props.disabled,
175        }
176    }
177
178    fn update(&mut self, msg: Self::Message) -> bool {
179        match msg {
180            Msg::Change => {
181                self.on_change.emit(());
182            }
183        }
184
185        false
186    }
187
188    fn change(&mut self, _props: Self::Properties) -> bool {
189        self.label = _props.label;
190        self.state = _props.state;
191        self.validation_message = _props.validation_message;
192        self.on_change = _props.on_change;
193        self.checked = _props.checked;
194        self.group = _props.group;
195        self.value = _props.value;
196        self.disabled = _props.disabled;
197
198        true
199    }
200
201    fn view(&self) -> Html {
202        let id = super::super::super::id_generator::generate_id();
203        html! {
204            <div class=self.get_radio_container_class()>
205                <div class="jinya-radio__container">
206                    <input
207                        id=id
208                        type="radio"
209                        onchange=self.link.callback(|_| Msg::Change)
210                        checked=self.checked
211                        name=&self.group
212                        class="jinya-radio__input"
213                        value=&self.value
214                        disabled=self.disabled
215                    />
216                    <label for=id class="jinya-radio__label">{&self.label}</label>
217                </div>
218                <span class="jinya-radio__validation-message">{&self.validation_message}</span>
219            </div>
220        }
221    }
222}