ui/components/
file_input.rs1use gpui::{AnyElement, ClickEvent, ElementId, IntoElement};
2
3use crate::prelude::*;
4
5#[derive(IntoElement, RegisterComponent)]
16pub struct FileInput {
17 id: ElementId,
18 label: SharedString,
19 hint: Option<SharedString>,
20 disabled: bool,
21 on_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
22}
23
24impl FileInput {
25 pub fn new(id: impl Into<ElementId>) -> Self {
26 Self {
27 id: id.into(),
28 label: "Click to upload".into(),
29 hint: None,
30 disabled: false,
31 on_click: None,
32 }
33 }
34
35 pub fn label(mut self, label: impl Into<SharedString>) -> Self {
36 self.label = label.into();
37 self
38 }
39
40 pub fn hint(mut self, hint: impl Into<SharedString>) -> Self {
41 self.hint = Some(hint.into());
42 self
43 }
44
45 pub fn disabled(mut self, disabled: bool) -> Self {
46 self.disabled = disabled;
47 self
48 }
49
50 pub fn on_click(
53 mut self,
54 handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
55 ) -> Self {
56 self.on_click = Some(Box::new(handler));
57 self
58 }
59}
60
61impl RenderOnce for FileInput {
62 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
63 v_flex()
64 .id(self.id)
65 .items_center()
66 .justify_center()
67 .gap_2()
68 .w_full()
69 .py_8()
70 .rounded_lg()
71 .border_2()
72 .border_dashed()
73 .border_color(semantic::border(cx))
74 .when(!self.disabled, |this| this.cursor_pointer())
75 .when(self.disabled, |this| this.opacity(0.5))
76 .child(
77 Icon::new(IconName::Paperclip)
78 .size(IconSize::Medium)
79 .color(Color::Muted),
80 )
81 .child(Label::new(self.label).size(LabelSize::Small))
82 .when_some(self.hint, |this, hint| {
83 this.child(Label::new(hint).size(LabelSize::XSmall).color(Color::Muted))
84 })
85 .when_some(self.on_click.filter(|_| !self.disabled), |this, handler| {
86 this.on_click(handler)
87 })
88 }
89}
90
91impl Component for FileInput {
92 fn scope() -> ComponentScope {
93 ComponentScope::Input
94 }
95
96 fn description() -> Option<&'static str> {
97 Some(
98 "A styled file-upload trigger. No OS file-dialog API exists in this codebase — \
99 purely presentational, caller wires `on_click` to their own integration.",
100 )
101 }
102
103 fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
104 Some(
105 v_flex()
106 .gap_4()
107 .child(
108 FileInput::new("file-input-default")
109 .hint("PNG, JPG up to 10MB")
110 .on_click(|_, _, _| {})
111 .into_any_element(),
112 )
113 .child(
114 FileInput::new("file-input-selected")
115 .label("resume.pdf")
116 .hint("245 KB — click to replace")
117 .on_click(|_, _, _| {})
118 .into_any_element(),
119 )
120 .child(
121 FileInput::new("file-input-disabled")
122 .disabled(true)
123 .into_any_element(),
124 )
125 .into_any_element(),
126 )
127 }
128}