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: &'static str,
 17    value: String,
 18    state: InteractionState,
 19    variant: InputVariant,
 20}
 21
 22impl<S: 'static + Send + Sync> Input<S> {
 23    pub fn new(placeholder: &'static str) -> Self {
 24        Self {
 25            state_type: PhantomData,
 26            placeholder,
 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, cx: &mut ViewContext<S>) -> impl Element<State = 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 mut border_color_active = theme.middle.base.pressed.border;
 59        let border_color_focus = theme.middle.base.pressed.background;
 60
 61        match self.variant {
 62            InputVariant::Ghost => {
 63                background_color_default = theme.middle.base.default.background;
 64                background_color_active = theme.middle.base.active.background;
 65            }
 66            InputVariant::Filled => {
 67                background_color_default = theme.middle.on.default.background;
 68                background_color_active = theme.middle.on.active.background;
 69            }
 70        };
 71
 72        if self.state == InteractionState::Focused {
 73            border_color_default = theme.players[0].cursor;
 74            border_color_hover = theme.players[0].cursor;
 75            border_color_active = theme.players[0].cursor;
 76        }
 77
 78        if self.state == InteractionState::Focused || self.state == InteractionState::Active {
 79            text_el = self.value.clone();
 80            text_color = theme.lowest.base.default.foreground;
 81        } else {
 82            text_el = self.placeholder.to_string().clone();
 83            text_color = theme.lowest.base.disabled.foreground;
 84        }
 85
 86        div()
 87            .h_7()
 88            .w_full()
 89            .px_2()
 90            .border()
 91            .border_color(border_color_default)
 92            .fill(background_color_default)
 93            .hover()
 94            .border_color(border_color_hover)
 95            // .active()
 96            // .border_color(border_color_active)
 97            .fill(background_color_active)
 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(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
134            Story::container(cx)
135                .child(Story::title_for::<_, Input<S>>(cx))
136                .child(Story::label(cx, "Default"))
137                .child(div().flex().child(Input::new("Search")))
138        }
139    }
140}