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            .h_7()
 86            .w_full()
 87            .px_2()
 88            .border()
 89            .border_color(border_color_default)
 90            .bg(background_color_default)
 91            .hover(|style| {
 92                style
 93                    .border_color(border_color_hover)
 94                    .bg(background_color_active)
 95            })
 96            // .active(|a| .border_color(border_color_active))
 97            .flex()
 98            .items_center()
 99            .child(
100                div()
101                    .flex()
102                    .items_center()
103                    .text_sm()
104                    .text_color(text_color)
105                    .child(text_el)
106                    .child(div().text_color(theme.players[0].cursor).child("|")),
107            )
108    }
109}
110
111#[cfg(feature = "stories")]
112pub use stories::*;
113
114#[cfg(feature = "stories")]
115mod stories {
116    use crate::Story;
117
118    use super::*;
119
120    #[derive(Element)]
121    pub struct InputStory<S: 'static + Send + Sync + Clone> {
122        state_type: PhantomData<S>,
123    }
124
125    impl<S: 'static + Send + Sync + Clone> InputStory<S> {
126        pub fn new() -> Self {
127            Self {
128                state_type: PhantomData,
129            }
130        }
131
132        fn render(
133            &mut self,
134            _view: &mut S,
135            cx: &mut ViewContext<S>,
136        ) -> impl Element<ViewState = S> {
137            Story::container(cx)
138                .child(Story::title_for::<_, Input<S>>(cx))
139                .child(Story::label(cx, "Default"))
140                .child(div().flex().child(Input::new("Search")))
141        }
142    }
143}