Skip to main content

dioxus_bulma/components/
file.rs

1use dioxus::prelude::*;
2use crate::theme::{BulmaColor, BulmaSize};
3use crate::utils::build_class;
4
5#[derive(Props, Clone, PartialEq)]
6pub struct FileProps {
7    #[props(default)]
8    pub color: Option<BulmaColor>,
9    #[props(default)]
10    pub size: Option<BulmaSize>,
11    #[props(default)]
12    pub boxed: Option<bool>,
13    #[props(default)]
14    pub centered: Option<bool>,
15    #[props(default)]
16    pub right: Option<bool>,
17    #[props(default)]
18    pub fullwidth: Option<bool>,
19    #[props(default)]
20    pub has_name: Option<bool>,
21    #[props(default)]
22    pub accept: Option<String>,
23    #[props(default)]
24    pub multiple: Option<bool>,
25    #[props(default)]
26    pub disabled: Option<bool>,
27    #[props(default)]
28    pub name: Option<String>,
29    #[props(default)]
30    pub filename: Option<String>,
31    #[props(default)]
32    pub onchange: Option<EventHandler<FormEvent>>,
33    #[props(default)]
34    pub id: Option<String>,
35    #[props(default)]
36    pub class: Option<String>,
37    #[props(default)]
38    pub style: Option<String>,
39    pub children: Element,
40}
41
42#[component]
43pub fn File(props: FileProps) -> Element {
44    let size = props.size.unwrap_or_default();
45    let boxed = props.boxed.unwrap_or(false);
46    let centered = props.centered.unwrap_or(false);
47    let right = props.right.unwrap_or(false);
48    let fullwidth = props.fullwidth.unwrap_or(false);
49    let has_name = props.has_name.unwrap_or(false);
50    let multiple = props.multiple.unwrap_or(false);
51    let disabled = props.disabled.unwrap_or(false);
52    
53    let base_classes = vec!["file"];
54    let optional_classes = vec![
55        props.color.map(|c| format!("is-{}", c.as_str())),
56        if size != BulmaSize::Normal { Some(size.as_class().to_string()) } else { None },
57        if boxed { Some("is-boxed".to_string()) } else { None },
58        if centered { Some("is-centered".to_string()) } else { None },
59        if right { Some("is-right".to_string()) } else { None },
60        if fullwidth { Some("is-fullwidth".to_string()) } else { None },
61        if has_name { Some("has-name".to_string()) } else { None },
62        props.class.clone(),
63    ];
64    
65    let final_class = build_class(&base_classes, &optional_classes);
66    let file_style = props.style.as_deref().unwrap_or("");
67
68    rsx! {
69        div {
70            class: "{final_class}",
71            style: "{file_style}",
72            id: props.id.clone(),
73            label {
74                class: "file-label",
75                input {
76                    class: "file-input",
77                    r#type: "file",
78                    accept: props.accept.as_deref(),
79                    multiple: multiple,
80                    disabled: disabled,
81                    name: props.name.as_deref(),
82                    onchange: move |evt| {
83                        if let Some(handler) = &props.onchange {
84                            handler.call(evt);
85                        }
86                    }
87                }
88                span {
89                    class: "file-cta",
90                    span {
91                        class: "file-icon",
92                        i { class: "fas fa-upload" }
93                    }
94                    span {
95                        class: "file-label",
96                        {props.children}
97                    }
98                }
99                if has_name {
100                    span {
101                        class: "file-name",
102                        {props.filename.as_deref().unwrap_or("No file chosen")}
103                    }
104                }
105            }
106        }
107    }
108}