input.rs

  1use std::marker::PhantomData;
  2
  3use crate::prelude::*;
  4use crate::theme;
  5
  6#[derive(Default, PartialEq)]
  7pub enum InputVariant {
  8    #[default]
  9    Ghost,
 10    Filled,
 11}
 12
 13#[derive(Element)]
 14pub struct Input<S: 'static + Send + Sync> {
 15    state_type: PhantomData<S>,
 16    placeholder: SharedString,
 17    value: String,
 18    state: InteractionState,
 19    variant: InputVariant,
 20}
 21
 22impl<S: 'static + Send + Sync> Input<S> {
 23    pub fn new(placeholder: impl Into<SharedString>) -> Self {
 24        Self {
 25            state_type: PhantomData,
 26            placeholder: placeholder.into(),
 27            value: "".to_string(),
 28            state: InteractionState::default(),
 29            variant: InputVariant::default(),
 30        }
 31    }
 32
 33    pub fn value(mut self, value: String) -> Self {
 34        self.value = value;
 35        self
 36    }
 37
 38    pub fn state(mut self, state: InteractionState) -> Self {
 39        self.state = state;
 40        self
 41    }
 42
 43    pub fn variant(mut self, variant: InputVariant) -> Self {
 44        self.variant = variant;
 45        self
 46    }
 47
 48    fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
 49        let theme = theme(cx);
 50
 51        let text_el;
 52        let text_color;
 53        let background_color_default;
 54        let background_color_active;
 55
 56        let mut border_color_default = theme.middle.base.default.border;
 57        let mut border_color_hover = theme.middle.base.hovered.border;
 58        let border_color_focus = theme.middle.base.pressed.background;
 59
 60        match self.variant {
 61            InputVariant::Ghost => {
 62                background_color_default = theme.middle.base.default.background;
 63                background_color_active = theme.middle.base.active.background;
 64            }
 65            InputVariant::Filled => {
 66                background_color_default = theme.middle.on.default.background;
 67                background_color_active = theme.middle.on.active.background;
 68            }
 69        };
 70
 71        if self.state == InteractionState::Focused {
 72            border_color_default = theme.players[0].cursor;
 73            border_color_hover = theme.players[0].cursor;
 74        }
 75
 76        if self.state == InteractionState::Focused || self.state == InteractionState::Active {
 77            text_el = self.value.clone();
 78            text_color = theme.lowest.base.default.foreground;
 79        } else {
 80            text_el = self.placeholder.to_string().clone();
 81            text_color = theme.lowest.base.disabled.foreground;
 82        }
 83
 84        div()
 85            .id("input")
 86            .h_7()
 87            .w_full()
 88            .px_2()
 89            .border()
 90            .border_color(border_color_default)
 91            .bg(background_color_default)
 92            .hover(|style| {
 93                style
 94                    .border_color(border_color_hover)
 95                    .bg(background_color_active)
 96            })
 97            .active(|style| style.border_color(theme.middle.base.active.border))
 98            .flex()
 99            .items_center()
100            .child(
101                div()
102                    .flex()
103                    .items_center()
104                    .text_sm()
105                    .text_color(text_color)
106                    .child(text_el)
107                    .child(div().text_color(theme.players[0].cursor).child("|")),
108            )
109    }
110}
111
112#[cfg(feature = "stories")]
113pub use stories::*;
114
115#[cfg(feature = "stories")]
116mod stories {
117    use crate::Story;
118
119    use super::*;
120
121    #[derive(Element)]
122    pub struct InputStory<S: 'static + Send + Sync + Clone> {
123        state_type: PhantomData<S>,
124    }
125
126    impl<S: 'static + Send + Sync + Clone> InputStory<S> {
127        pub fn new() -> Self {
128            Self {
129                state_type: PhantomData,
130            }
131        }
132
133        fn render(
134            &mut self,
135            _view: &mut S,
136            cx: &mut ViewContext<S>,
137        ) -> impl Element<ViewState = S> {
138            Story::container(cx)
139                .child(Story::title_for::<_, Input<S>>(cx))
140                .child(Story::label(cx, "Default"))
141                .child(div().flex().child(Input::new("Search")))
142        }
143    }
144}