Skip to main content

kayrx_ui/widget/
file_drop_area.rs

1use web_sys::DragEvent;
2use crate::fabric::prelude::*;
3use crate::fabric::services::reader::File;
4
5pub struct FileDropArea {
6    props: Props,
7    link: ComponentLink<Self>,
8    input_ref: NodeRef,
9}
10
11pub enum Msg {
12    Files(Vec<File>),
13    Nop,
14}
15
16#[derive(Clone, Properties)]
17pub struct Props {
18    #[prop_or_default]
19    pub disabled: bool,
20    #[prop_or_else(Callback::noop)]
21    pub onchange: Callback<Vec<File>>,
22    #[prop_or_default]
23    pub children: Children,
24}
25
26impl Component for FileDropArea {
27    type Message = Msg;
28    type Properties = Props;
29
30    fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
31        FileDropArea {
32            props,
33            link,
34            input_ref: NodeRef::default(),
35        }
36    }
37
38    fn update(&mut self, msg: Self::Message) -> ShouldRender {
39        match msg {
40            Msg::Files(v) => {
41                self.props.onchange.emit(v);
42                return true;
43            }
44            Msg::Nop => {}
45        }
46        true
47    }
48
49    fn change(&mut self, props: Self::Properties) -> ShouldRender {
50        self.props = props;
51        true
52    }
53
54    fn view(&self) -> Html {
55        let ondragover = self.link.callback(|e: DragEvent| {
56            e.prevent_default();
57            Msg::Nop
58        });
59        let ondrop = self.link.callback(|e: DragEvent| {
60            e.prevent_default();
61            if let Some(ft) = e.data_transfer() {
62                return Msg::Files(
63                    js_sys::try_iter(&ft.files().unwrap())
64                        .unwrap()
65                        .unwrap()
66                        .map(|v| File::from(v.unwrap()))
67                        .collect(),
68                );
69            }
70
71            Msg::Nop
72        });
73        let onchange = self.link.callback(|e| {
74            let res = match e {
75                ChangeData::Files(f) => js_sys::try_iter(&f)
76                    .unwrap()
77                    .unwrap()
78                    .map(|v| File::from(v.unwrap()))
79                    .collect(),
80                _ => unreachable!(),
81            };
82            Msg::Files(res)
83        });
84        html! {
85            <div class="bow-drop-area"
86                ondrop=ondrop
87                ondragover=ondragover
88                disabled=self.props.disabled>
89
90                <input type="file" hidden=true
91                ref=self.input_ref.clone(),
92                multiple=true
93                onchange=onchange></input>
94
95                { self.props.children.render() }
96            </div>
97        }
98    }
99}