jinya_ui/widgets/form/
dropdown.rs

1use yew::prelude::*;
2use yew::{Callback, Component, ComponentLink, Html};
3
4pub fn get_css<'a>() -> &'a str {
5    // language=CSS
6    "
7.jinya-dropdown__color-container {
8    width: 100%;
9}
10
11.jinya-dropdown__color-container--default {
12    --state-color: var(--primary-color);
13    --select-arrow: var(--background-image-select-primary);
14}
15
16.jinya-dropdown__color-container--negative {
17    --state-color: var(--negative-color);
18    --select-arrow: var(--background-image-select-negative);
19}
20
21.jinya-dropdown__color-container--positive {
22    --state-color: var(--positive-color);
23    --select-arrow: var(--background-image-select-positive);
24}
25
26.jinya-dropdown__color-container--disabled {
27    --state-color: var(--disabled-border-color);
28    --select-arrow: var(--background-image-select-disabled);
29}
30
31.jinya-dropdown__container {
32    display: inline-block;
33    border: 2px solid var(--state-color);
34    border-radius: 5px;
35    padding: 0.5rem 0.75rem 0.25rem;
36    position: relative;
37    margin-top: 0.75rem;
38    width: 100%;
39    box-sizing: border-box;
40}
41
42.jinya-dropdown__select:disabled {
43    cursor: not-allowed;
44}
45
46.jinya-dropdown__select {
47    font-size: var(--font-size-16);
48    color: var(--state-color);
49    background: var(--white);
50    font-family: var(--font-family);
51    border: none;
52    padding: 0;
53    width: 100%;
54    /* for Firefox */
55    -moz-appearance: none;
56    /* for Chrome */
57    -webkit-appearance: none;
58    background-image: var(--select-arrow);
59    background-repeat: no-repeat;
60    background-position-x: right;
61    background-position-y: center;
62    outline: none;
63}
64
65.jinya-dropdown__select:invalid {
66    outline: none;
67    box-shadow: none;
68    border: none;
69}
70
71.jinya-dropdown__label {
72    display: block;
73    font-size: var(--font-size-12);
74    color: var(--state-color);
75    position: absolute;
76    top: -0.75rem;
77    background: var(--white);
78    padding-left: 0.25rem;
79    padding-right: 0.25rem;
80    box-sizing: border-box;
81    left: 0.5rem;
82    z-index: 0;
83}
84
85.jinya-dropdown__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 DropdownState {
95    Default,
96    Negative,
97    Positive,
98}
99
100#[derive(Clone, PartialEq, Properties)]
101pub struct DropdownItem {
102    pub value: String,
103    pub text: String,
104}
105
106pub struct Dropdown {
107    link: ComponentLink<Self>,
108    label: String,
109    on_select: Callback<String>,
110    state: DropdownState,
111    dropdown_type: String,
112    validation_message: String,
113    items: Vec<DropdownItem>,
114    placeholder: Option<String>,
115    disabled: bool,
116    value: String,
117}
118
119#[derive(Clone, PartialEq, Properties)]
120pub struct DropdownProps {
121    pub label: String,
122    pub on_select: Callback<String>,
123    #[prop_or(DropdownState::Default)]
124    pub state: DropdownState,
125    #[prop_or("text".to_string())]
126    pub dropdown_type: String,
127    #[prop_or("".to_string())]
128    pub validation_message: String,
129    pub items: Vec<DropdownItem>,
130    #[prop_or(None)]
131    pub placeholder: Option<String>,
132    #[prop_or(false)]
133    pub disabled: bool,
134    pub value: String,
135}
136
137pub enum Msg {
138    Change(ChangeData),
139}
140
141impl Default for DropdownState {
142    fn default() -> Self {
143        DropdownState::Default
144    }
145}
146
147impl Dropdown {
148    fn get_dropdown_container_class(&self) -> String {
149        let class = match self.state {
150            DropdownState::Default => {
151                "jinya-dropdown__color-container jinya-dropdown__color-container--default"
152            }
153            DropdownState::Negative => {
154                "jinya-dropdown__color-container jinya-dropdown__color-container--negative"
155            }
156            DropdownState::Positive => {
157                "jinya-dropdown__color-container jinya-dropdown__color-container--positive"
158            }
159        }
160        .to_string();
161
162        if self.disabled {
163            "jinya-dropdown__color-container jinya-dropdown__color-container--disabled".to_string()
164        } else {
165            class
166        }
167    }
168}
169
170impl Component for Dropdown {
171    type Message = Msg;
172    type Properties = DropdownProps;
173
174    fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
175        Dropdown {
176            link,
177            label: props.label,
178            on_select: props.on_select,
179            state: props.state,
180            placeholder: props.placeholder,
181            dropdown_type: props.dropdown_type,
182            validation_message: props.validation_message,
183            items: props.items,
184            disabled: props.disabled,
185            value: props.value,
186        }
187    }
188
189    fn update(&mut self, msg: Self::Message) -> bool {
190        match msg {
191            Msg::Change(data) => {
192                let value = match data {
193                    ChangeData::Select(element) => element.value(),
194                    _ => unreachable!(),
195                };
196                self.on_select.emit(value);
197            }
198        }
199
200        false
201    }
202
203    fn change(&mut self, _props: Self::Properties) -> bool {
204        self.label = _props.label;
205        self.on_select = _props.on_select;
206        self.state = _props.state;
207        self.placeholder = _props.placeholder;
208        self.validation_message = _props.validation_message;
209        self.dropdown_type = _props.dropdown_type;
210        self.items = _props.items;
211        self.disabled = _props.disabled;
212        self.value = _props.value;
213
214        true
215    }
216
217    fn view(&self) -> Html {
218        let id = super::super::super::id_generator::generate_id();
219        html! {
220            <div class=self.get_dropdown_container_class()>
221                <div class="jinya-dropdown__container">
222                    <label for=id class="jinya-dropdown__label">{&self.label}</label>
223                    <select class="jinya-dropdown__select" disabled=self.disabled onchange=self.link.callback(|data: ChangeData| Msg::Change(data))>
224                    {if self.placeholder.is_some() {
225                        html! {
226                            <option value="">{self.placeholder.as_ref().unwrap()}</option>
227                        }
228                    } else {
229                        html! {}
230                    }}
231                    {for self.items.iter().map(|mut item| {
232                        html! {
233                            <option value=&item.value selected={self.value == item.value}>{&item.text}</option>
234                        }
235                    })}
236                    </select>
237                </div>
238                <span class="jinya-dropdown__validation-message">{&self.validation_message}</span>
239            </div>
240        }
241    }
242}