1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
use crate::{prelude::*, Label};
use gpui::{prelude::*, Div, RenderOnce, Stateful};

#[derive(Default, PartialEq)]
pub enum InputVariant {
    #[default]
    Ghost,
    Filled,
}

#[derive(RenderOnce)]
pub struct Input {
    placeholder: SharedString,
    value: String,
    state: InteractionState,
    variant: InputVariant,
    disabled: bool,
    is_active: bool,
}

impl Component for Input {
    type Rendered = Stateful<Div>;

    fn render(self, cx: &mut WindowContext) -> Self::Rendered {
        let (input_bg, input_hover_bg, input_active_bg) = match self.variant {
            InputVariant::Ghost => (
                cx.theme().colors().ghost_element_background,
                cx.theme().colors().ghost_element_hover,
                cx.theme().colors().ghost_element_active,
            ),
            InputVariant::Filled => (
                cx.theme().colors().element_background,
                cx.theme().colors().element_hover,
                cx.theme().colors().element_active,
            ),
        };

        let placeholder_label = Label::new(self.placeholder.clone()).color(if self.disabled {
            Color::Disabled
        } else {
            Color::Placeholder
        });

        let label = Label::new(self.value.clone()).color(if self.disabled {
            Color::Disabled
        } else {
            Color::Default
        });

        div()
            .id("input")
            .h_7()
            .w_full()
            .px_2()
            .border()
            .border_color(cx.theme().styles.system.transparent)
            .bg(input_bg)
            .hover(|style| style.bg(input_hover_bg))
            .active(|style| style.bg(input_active_bg))
            .flex()
            .items_center()
            .child(div().flex().items_center().text_ui_sm().map(move |this| {
                if self.value.is_empty() {
                    this.child(placeholder_label)
                } else {
                    this.child(label)
                }
            }))
    }
}

impl Input {
    pub fn new(placeholder: impl Into<SharedString>) -> Self {
        Self {
            placeholder: placeholder.into(),
            value: "".to_string(),
            state: InteractionState::default(),
            variant: InputVariant::default(),
            disabled: false,
            is_active: false,
        }
    }

    pub fn value(mut self, value: String) -> Self {
        self.value = value;
        self
    }

    pub fn state(mut self, state: InteractionState) -> Self {
        self.state = state;
        self
    }

    pub fn variant(mut self, variant: InputVariant) -> Self {
        self.variant = variant;
        self
    }

    pub fn disabled(mut self, disabled: bool) -> Self {
        self.disabled = disabled;
        self
    }

    pub fn is_active(mut self, is_active: bool) -> Self {
        self.is_active = is_active;
        self
    }
}