Checkpoint

Nate Butler created

Change summary

crates/storybook/src/prelude.rs                 |  7 +
crates/storybook/src/ui.rs                      |  3 
crates/storybook/src/ui/element.rs              |  2 
crates/storybook/src/ui/element/input.rs        | 99 +++++++++++++++++++
crates/storybook/src/ui/element/label.rs        | 49 +++++++++
crates/storybook/src/ui/module.rs               |  1 
crates/storybook/src/ui/module/project_panel.rs | 65 ++++++++++++
crates/storybook/src/workspace.rs               |  5 
8 files changed, 228 insertions(+), 3 deletions(-)

Detailed changes

crates/storybook/src/prelude.rs 🔗

@@ -5,6 +5,13 @@ pub enum ButtonVariant {
     Filled,
 }
 
+#[derive(Default, PartialEq)]
+pub enum InputVariant {
+    #[default]
+    Ghost,
+    Filled,
+}
+
 #[derive(Default, PartialEq, Clone, Copy)]
 pub enum Shape {
     #[default]

crates/storybook/src/ui.rs 🔗

@@ -7,6 +7,7 @@ pub use component::follow_group::*;
 pub use component::tab::*;
 
 pub use module::chat_panel::*;
+pub use module::project_panel::*;
 pub use module::status_bar::*;
 pub use module::tab_bar::*;
 pub use module::title_bar::*;
@@ -14,5 +15,7 @@ pub use module::title_bar::*;
 pub use element::avatar::*;
 pub use element::icon_button::*;
 pub use element::indicator::*;
+pub use element::input::*;
+pub use element::label::*;
 pub use element::text_button::*;
 pub use element::tool_divider::*;

crates/storybook/src/ui/element.rs 🔗

@@ -1,5 +1,7 @@
 pub(crate) mod avatar;
 pub(crate) mod icon_button;
 pub(crate) mod indicator;
+pub(crate) mod input;
+pub(crate) mod label;
 pub(crate) mod text_button;
 pub(crate) mod tool_divider;

crates/storybook/src/ui/element/input.rs 🔗

@@ -0,0 +1,99 @@
+use crate::prelude::{InputVariant, InteractionState};
+use crate::theme::theme;
+use gpui2::style::{StyleHelpers, Styleable};
+use gpui2::{elements::div, IntoElement};
+use gpui2::{Element, ParentElement, ViewContext};
+
+#[derive(Element)]
+pub struct Input {
+    placeholder: &'static str,
+    value: String,
+    state: InteractionState,
+    variant: InputVariant,
+}
+
+pub fn input(placeholder: &'static str) -> Input {
+    Input {
+        placeholder,
+        value: "".to_string(),
+        state: InteractionState::default(),
+        variant: InputVariant::default(),
+    }
+}
+
+impl Input {
+    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
+    }
+
+    fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
+        let theme = theme(cx);
+
+        let text_el;
+        let text_color;
+        let background_color_default;
+        let background_color_active;
+
+        let mut border_color_default = theme.middle.base.default.border;
+        let mut border_color_hover = theme.middle.base.hovered.border;
+        let mut border_color_active = theme.middle.base.pressed.border;
+        let border_color_focus = theme.middle.base.pressed.background;
+
+        match self.variant {
+            InputVariant::Ghost => {
+                background_color_default = theme.middle.base.default.background;
+                background_color_active = theme.middle.base.active.background;
+            }
+            InputVariant::Filled => {
+                background_color_default = theme.middle.on.default.background;
+                background_color_active = theme.middle.on.active.background;
+            }
+        };
+
+        if self.state == InteractionState::Focused {
+            border_color_default = theme.players[0].cursor;
+            border_color_hover = theme.players[0].cursor;
+            border_color_active = theme.players[0].cursor;
+        }
+
+        if self.state == InteractionState::Focused || self.state == InteractionState::Active {
+            text_el = self.value.clone();
+            text_color = theme.lowest.base.default.foreground;
+        } else {
+            text_el = self.placeholder.to_string().clone();
+            text_color = theme.lowest.base.disabled.foreground;
+        }
+
+        div()
+            .h_7()
+            .px_2()
+            .border()
+            .border_color(border_color_default)
+            .fill(background_color_default)
+            .hover()
+            .border_color(border_color_hover)
+            .active()
+            .border_color(border_color_active)
+            .fill(background_color_active)
+            .flex()
+            .items_center()
+            .child(
+                div()
+                    .flex()
+                    .items_center()
+                    .text_sm()
+                    .text_color(text_color)
+                    .child(text_el)
+                    .child(div().text_color(theme.players[0].cursor).child("|")),
+            )
+    }
+}

crates/storybook/src/ui/element/label.rs 🔗

@@ -0,0 +1,49 @@
+use crate::theme::theme;
+use gpui2::elements::div;
+use gpui2::style::StyleHelpers;
+use gpui2::{Element, ViewContext};
+use gpui2::{IntoElement, ParentElement};
+
+#[derive(Default, PartialEq, Copy, Clone)]
+pub enum LabelColor {
+    #[default]
+    Default,
+    Created,
+    Modified,
+    Deleted,
+    Hidden,
+}
+
+#[derive(Element, Clone)]
+pub struct Label {
+    label: &'static str,
+    color: LabelColor,
+}
+
+pub fn label(label: &'static str) -> Label {
+    Label {
+        label: "Label",
+        color: LabelColor::Default,
+    }
+}
+
+impl Label {
+    pub fn color(mut self, color: LabelColor) -> Self {
+        self.color = color;
+        self
+    }
+
+    fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
+        let theme = theme(cx);
+
+        let color = match self.color {
+            LabelColor::Default => theme.lowest.base.default.foreground,
+            LabelColor::Created => theme.lowest.positive.default.foreground,
+            LabelColor::Modified => theme.lowest.warning.default.foreground,
+            LabelColor::Deleted => theme.lowest.negative.default.foreground,
+            LabelColor::Hidden => theme.lowest.variant.default.foreground,
+        };
+
+        div().text_sm().text_color(color).child(self.label.clone())
+    }
+}

crates/storybook/src/ui/module.rs 🔗

@@ -1,4 +1,5 @@
 pub(crate) mod chat_panel;
+pub(crate) mod project_panel;
 pub(crate) mod status_bar;
 pub(crate) mod tab_bar;
 pub(crate) mod title_bar;

crates/storybook/src/ui/module/project_panel.rs 🔗

@@ -0,0 +1,65 @@
+use crate::{
+    prelude::InteractionState,
+    theme::theme,
+    ui::{input, label, LabelColor},
+};
+use gpui2::{
+    elements::{div, div::ScrollState},
+    style::StyleHelpers,
+    ParentElement, ViewContext,
+};
+use gpui2::{Element, IntoElement};
+use std::marker::PhantomData;
+
+#[derive(Element)]
+pub struct ProjectPanel<V: 'static> {
+    view_type: PhantomData<V>,
+    scroll_state: ScrollState,
+}
+
+pub fn project_panel<V: 'static>(scroll_state: ScrollState) -> ProjectPanel<V> {
+    ProjectPanel {
+        view_type: PhantomData,
+        scroll_state,
+    }
+}
+
+impl<V: 'static> ProjectPanel<V> {
+    fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
+        let theme = theme(cx);
+
+        div()
+            .w_64()
+            .h_full()
+            .flex()
+            .flex_col()
+            .fill(theme.middle.base.default.background)
+            .child(
+                div()
+                    .w_full()
+                    .flex()
+                    .flex_col()
+                    .overflow_y_scroll(self.scroll_state.clone())
+                    .child(
+                        div().py_2().flex().flex_col().children(
+                            std::iter::repeat_with(|| {
+                                vec![
+                                    label("File"),
+                                    label("Modified File").color(LabelColor::Modified),
+                                    label("Created File").color(LabelColor::Created),
+                                    label("Deleted File").color(LabelColor::Deleted),
+                                    label("Hidden File").color(LabelColor::Hidden),
+                                ]
+                            })
+                            .take(60)
+                            .flatten(),
+                        ),
+                    ),
+            )
+            .child(
+                input("Find something...")
+                    .value("buffe".to_string())
+                    .state(InteractionState::Focused),
+            )
+    }
+}

crates/storybook/src/workspace.rs 🔗

@@ -1,7 +1,6 @@
 use crate::{
-    collab_panel::collab_panel,
     theme::theme,
-    ui::{chat_panel, status_bar, tab_bar, title_bar},
+    ui::{chat_panel, project_panel, status_bar, tab_bar, title_bar},
 };
 use gpui2::{
     elements::{div, div::ScrollState},
@@ -42,7 +41,7 @@ impl WorkspaceElement {
                     .flex()
                     .flex_row()
                     .overflow_hidden()
-                    .child(collab_panel(self.left_scroll_state.clone()))
+                    .child(project_panel(self.left_scroll_state.clone()))
                     .child(
                         div()
                             .h_full()