From 56c2ac048db7818a1a3953aa5bb77572a90222f2 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Fri, 6 Oct 2023 17:58:23 -0400 Subject: [PATCH] Add `ProjectPanel` component --- crates/storybook2/src/stories/components.rs | 1 + .../src/stories/components/project_panel.rs | 30 ++++ crates/storybook2/src/stories/elements.rs | 1 + .../storybook2/src/stories/elements/input.rs | 26 ++++ crates/storybook2/src/story_selector.rs | 4 + crates/ui2/src/components.rs | 2 + crates/ui2/src/components/project_panel.rs | 58 +++++++ crates/ui2/src/elements.rs | 2 + crates/ui2/src/elements/input.rs | 110 +++++++++++++ crates/ui2/src/static_data.rs | 146 +++++++++++++++++- 10 files changed, 379 insertions(+), 1 deletion(-) create mode 100644 crates/storybook2/src/stories/components/project_panel.rs create mode 100644 crates/storybook2/src/stories/elements/input.rs create mode 100644 crates/ui2/src/components/project_panel.rs create mode 100644 crates/ui2/src/elements/input.rs diff --git a/crates/storybook2/src/stories/components.rs b/crates/storybook2/src/stories/components.rs index a7c42c0b609c3c01276580098d63b932f5696376..0097c67e0e688d61dfc80dd712ebf1761e498685 100644 --- a/crates/storybook2/src/stories/components.rs +++ b/crates/storybook2/src/stories/components.rs @@ -1,3 +1,4 @@ pub mod assistant_panel; pub mod buffer; pub mod panel; +pub mod project_panel; diff --git a/crates/storybook2/src/stories/components/project_panel.rs b/crates/storybook2/src/stories/components/project_panel.rs new file mode 100644 index 0000000000000000000000000000000000000000..c2fb4edd4925ca8070d11be451556b010473b16d --- /dev/null +++ b/crates/storybook2/src/stories/components/project_panel.rs @@ -0,0 +1,30 @@ +use std::marker::PhantomData; + +use ui::prelude::*; +use ui::{Panel, ProjectPanel}; + +use crate::story::Story; + +#[derive(Element)] +pub struct ProjectPanelStory { + state_type: PhantomData, +} + +impl ProjectPanelStory { + pub fn new() -> Self { + Self { + state_type: PhantomData, + } + } + + fn render(&mut self, cx: &mut ViewContext) -> impl Element { + Story::container(cx) + .child(Story::title_for::<_, ProjectPanel>(cx)) + .child(Story::label(cx, "Default")) + .child(Panel::new( + ScrollState::default(), + |_, _| vec![ProjectPanel::new(ScrollState::default()).into_any()], + Box::new(()), + )) + } +} diff --git a/crates/storybook2/src/stories/elements.rs b/crates/storybook2/src/stories/elements.rs index 5579cff93ee3fbcc0d4c83c7f9d2ef0de1e84247..e177412a7ea248328f9ddc08eaefbe38064866f4 100644 --- a/crates/storybook2/src/stories/elements.rs +++ b/crates/storybook2/src/stories/elements.rs @@ -1,3 +1,4 @@ pub mod avatar; pub mod icon; +pub mod input; pub mod label; diff --git a/crates/storybook2/src/stories/elements/input.rs b/crates/storybook2/src/stories/elements/input.rs new file mode 100644 index 0000000000000000000000000000000000000000..4e45bc613a65ba753ecfad71d8b226ffc3c61c51 --- /dev/null +++ b/crates/storybook2/src/stories/elements/input.rs @@ -0,0 +1,26 @@ +use std::marker::PhantomData; + +use ui::prelude::*; +use ui::Input; + +use crate::story::Story; + +#[derive(Element)] +pub struct InputStory { + state_type: PhantomData, +} + +impl InputStory { + pub fn new() -> Self { + Self { + state_type: PhantomData, + } + } + + fn render(&mut self, cx: &mut ViewContext) -> impl Element { + Story::container(cx) + .child(Story::title_for::<_, Input>(cx)) + .child(Story::label(cx, "Default")) + .child(div().flex().child(Input::new("Search"))) + } +} diff --git a/crates/storybook2/src/story_selector.rs b/crates/storybook2/src/story_selector.rs index 8c6aac4b2cb0d4661b5bc6e3ee40b8d8b6ba4572..519befa7cd053b006156f92a20651525d2a702da 100644 --- a/crates/storybook2/src/story_selector.rs +++ b/crates/storybook2/src/story_selector.rs @@ -14,6 +14,7 @@ use ui::prelude::*; pub enum ElementStory { Avatar, Icon, + Input, Label, } @@ -24,6 +25,7 @@ impl ElementStory { match self { Self::Avatar => elements::avatar::AvatarStory::new().into_any(), Self::Icon => elements::icon::IconStory::new().into_any(), + Self::Input => elements::input::InputStory::new().into_any(), Self::Label => elements::label::LabelStory::new().into_any(), } } @@ -35,6 +37,7 @@ pub enum ComponentStory { AssistantPanel, Buffer, Panel, + ProjectPanel, } impl ComponentStory { @@ -47,6 +50,7 @@ impl ComponentStory { } Self::Buffer => components::buffer::BufferStory::new().into_any(), Self::Panel => components::panel::PanelStory::new().into_any(), + Self::ProjectPanel => components::project_panel::ProjectPanelStory::new().into_any(), } } } diff --git a/crates/ui2/src/components.rs b/crates/ui2/src/components.rs index 5667df6b385325eba10d7eeedf8bd118170b35ac..993f0799fbf91c999c54cc2eade12facb6e44410 100644 --- a/crates/ui2/src/components.rs +++ b/crates/ui2/src/components.rs @@ -3,9 +3,11 @@ mod buffer; mod icon_button; mod list; mod panel; +mod project_panel; pub use assistant_panel::*; pub use buffer::*; pub use icon_button::*; pub use list::*; pub use panel::*; +pub use project_panel::*; diff --git a/crates/ui2/src/components/project_panel.rs b/crates/ui2/src/components/project_panel.rs new file mode 100644 index 0000000000000000000000000000000000000000..59a81ace81369f91fb0f7b923e1ca3c90e365289 --- /dev/null +++ b/crates/ui2/src/components/project_panel.rs @@ -0,0 +1,58 @@ +use std::marker::PhantomData; + +use crate::prelude::*; +use crate::{ + static_project_panel_project_items, static_project_panel_single_items, theme, Input, List, + ListHeader, +}; + +#[derive(Element)] +pub struct ProjectPanel { + state_type: PhantomData, + scroll_state: ScrollState, +} + +impl ProjectPanel { + pub fn new(scroll_state: ScrollState) -> Self { + Self { + state_type: PhantomData, + scroll_state, + } + } + + fn render(&mut self, cx: &mut ViewContext) -> impl Element { + let theme = theme(cx); + + div() + .flex() + .flex_col() + .w_full() + .h_full() + .px_2() + .fill(theme.middle.base.default.background) + .child( + div() + .w_56() + .flex() + .flex_col() + .overflow_y_scroll(ScrollState::default()) + .child( + List::new(static_project_panel_single_items()) + .header(ListHeader::new("FILES").set_toggle(ToggleState::Toggled)) + .empty_message("No files in directory") + .set_toggle(ToggleState::Toggled), + ) + .child( + List::new(static_project_panel_project_items()) + .header(ListHeader::new("PROJECT").set_toggle(ToggleState::Toggled)) + .empty_message("No folders in directory") + .set_toggle(ToggleState::Toggled), + ), + ) + .child( + Input::new("Find something...") + .value("buffe".to_string()) + .state(InteractionState::Focused), + ) + } +} diff --git a/crates/ui2/src/elements.rs b/crates/ui2/src/elements.rs index 4346594c7c361354764f8b356e4e632204dfc283..9a80eb79cd4dcbf8deed9f84cf4d32789f2b3806 100644 --- a/crates/ui2/src/elements.rs +++ b/crates/ui2/src/elements.rs @@ -1,11 +1,13 @@ mod avatar; mod button; mod icon; +mod input; mod label; mod stack; pub use avatar::*; pub use button::*; pub use icon::*; +pub use input::*; pub use label::*; pub use stack::*; diff --git a/crates/ui2/src/elements/input.rs b/crates/ui2/src/elements/input.rs new file mode 100644 index 0000000000000000000000000000000000000000..afc653de73e8b6735b1ce072bbd2f8d05d2aa0a7 --- /dev/null +++ b/crates/ui2/src/elements/input.rs @@ -0,0 +1,110 @@ +use std::marker::PhantomData; + +use crate::prelude::*; +use crate::theme; + +#[derive(Default, PartialEq)] +pub enum InputVariant { + #[default] + Ghost, + Filled, +} + +#[derive(Element)] +pub struct Input { + state_type: PhantomData, + placeholder: &'static str, + value: String, + state: InteractionState, + variant: InputVariant, +} + +impl Input { + pub fn new(placeholder: &'static str) -> Self { + Self { + state_type: PhantomData, + placeholder, + value: "".to_string(), + state: InteractionState::default(), + variant: InputVariant::default(), + } + } + + 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(&mut self, cx: &mut ViewContext) -> impl Element { + 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() + .w_full() + .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("|")), + ) + } +} diff --git a/crates/ui2/src/static_data.rs b/crates/ui2/src/static_data.rs index 2950d9212921cbbd7384bd3a865f2cd4f9e954ca..409fce768f5c12e1c6071df8fe973b02246ce99c 100644 --- a/crates/ui2/src/static_data.rs +++ b/crates/ui2/src/static_data.rs @@ -1,8 +1,152 @@ use crate::{ Buffer, BufferRow, BufferRows, GitStatus, HighlightColor, HighlightedLine, HighlightedText, - Theme, + Icon, Label, LabelColor, ListEntry, ListItem, Theme, ToggleState, }; +pub fn static_project_panel_project_items() -> Vec> { + vec![ + ListEntry::new(Label::new("zed")) + .left_icon(Icon::FolderOpen.into()) + .indent_level(0) + .set_toggle(ToggleState::Toggled), + ListEntry::new(Label::new(".cargo")) + .left_icon(Icon::Folder.into()) + .indent_level(1), + ListEntry::new(Label::new(".config")) + .left_icon(Icon::Folder.into()) + .indent_level(1), + ListEntry::new(Label::new(".git").color(LabelColor::Hidden)) + .left_icon(Icon::Folder.into()) + .indent_level(1), + ListEntry::new(Label::new(".cargo")) + .left_icon(Icon::Folder.into()) + .indent_level(1), + ListEntry::new(Label::new(".idea").color(LabelColor::Hidden)) + .left_icon(Icon::Folder.into()) + .indent_level(1), + ListEntry::new(Label::new("assets")) + .left_icon(Icon::Folder.into()) + .indent_level(1) + .set_toggle(ToggleState::Toggled), + ListEntry::new(Label::new("cargo-target").color(LabelColor::Hidden)) + .left_icon(Icon::Folder.into()) + .indent_level(1), + ListEntry::new(Label::new("crates")) + .left_icon(Icon::FolderOpen.into()) + .indent_level(1) + .set_toggle(ToggleState::Toggled), + ListEntry::new(Label::new("activity_indicator")) + .left_icon(Icon::Folder.into()) + .indent_level(2), + ListEntry::new(Label::new("ai")) + .left_icon(Icon::Folder.into()) + .indent_level(2), + ListEntry::new(Label::new("audio")) + .left_icon(Icon::Folder.into()) + .indent_level(2), + ListEntry::new(Label::new("auto_update")) + .left_icon(Icon::Folder.into()) + .indent_level(2), + ListEntry::new(Label::new("breadcrumbs")) + .left_icon(Icon::Folder.into()) + .indent_level(2), + ListEntry::new(Label::new("call")) + .left_icon(Icon::Folder.into()) + .indent_level(2), + ListEntry::new(Label::new("sqlez").color(LabelColor::Modified)) + .left_icon(Icon::Folder.into()) + .indent_level(2) + .set_toggle(ToggleState::NotToggled), + ListEntry::new(Label::new("gpui2")) + .left_icon(Icon::FolderOpen.into()) + .indent_level(2) + .set_toggle(ToggleState::Toggled), + ListEntry::new(Label::new("src")) + .left_icon(Icon::FolderOpen.into()) + .indent_level(3) + .set_toggle(ToggleState::Toggled), + ListEntry::new(Label::new("derive_element.rs")) + .left_icon(Icon::FileRust.into()) + .indent_level(4), + ListEntry::new(Label::new("storybook").color(LabelColor::Modified)) + .left_icon(Icon::FolderOpen.into()) + .indent_level(1) + .set_toggle(ToggleState::Toggled), + ListEntry::new(Label::new("docs").color(LabelColor::Default)) + .left_icon(Icon::Folder.into()) + .indent_level(2) + .set_toggle(ToggleState::Toggled), + ListEntry::new(Label::new("src").color(LabelColor::Modified)) + .left_icon(Icon::FolderOpen.into()) + .indent_level(3) + .set_toggle(ToggleState::Toggled), + ListEntry::new(Label::new("ui").color(LabelColor::Modified)) + .left_icon(Icon::FolderOpen.into()) + .indent_level(4) + .set_toggle(ToggleState::Toggled), + ListEntry::new(Label::new("component").color(LabelColor::Created)) + .left_icon(Icon::FolderOpen.into()) + .indent_level(5) + .set_toggle(ToggleState::Toggled), + ListEntry::new(Label::new("facepile.rs").color(LabelColor::Default)) + .left_icon(Icon::FileRust.into()) + .indent_level(6), + ListEntry::new(Label::new("follow_group.rs").color(LabelColor::Default)) + .left_icon(Icon::FileRust.into()) + .indent_level(6), + ListEntry::new(Label::new("list_item.rs").color(LabelColor::Created)) + .left_icon(Icon::FileRust.into()) + .indent_level(6), + ListEntry::new(Label::new("tab.rs").color(LabelColor::Default)) + .left_icon(Icon::FileRust.into()) + .indent_level(6), + ListEntry::new(Label::new("target").color(LabelColor::Hidden)) + .left_icon(Icon::Folder.into()) + .indent_level(1), + ListEntry::new(Label::new(".dockerignore")) + .left_icon(Icon::FileGeneric.into()) + .indent_level(1), + ListEntry::new(Label::new(".DS_Store").color(LabelColor::Hidden)) + .left_icon(Icon::FileGeneric.into()) + .indent_level(1), + ListEntry::new(Label::new("Cargo.lock")) + .left_icon(Icon::FileLock.into()) + .indent_level(1), + ListEntry::new(Label::new("Cargo.toml")) + .left_icon(Icon::FileToml.into()) + .indent_level(1), + ListEntry::new(Label::new("Dockerfile")) + .left_icon(Icon::FileGeneric.into()) + .indent_level(1), + ListEntry::new(Label::new("Procfile")) + .left_icon(Icon::FileGeneric.into()) + .indent_level(1), + ListEntry::new(Label::new("README.md")) + .left_icon(Icon::FileDoc.into()) + .indent_level(1), + ] + .into_iter() + .map(From::from) + .collect() +} + +pub fn static_project_panel_single_items() -> Vec> { + vec![ + ListEntry::new(Label::new("todo.md")) + .left_icon(Icon::FileDoc.into()) + .indent_level(0), + ListEntry::new(Label::new("README.md")) + .left_icon(Icon::FileDoc.into()) + .indent_level(0), + ListEntry::new(Label::new("config.json")) + .left_icon(Icon::FileGeneric.into()) + .indent_level(0), + ] + .into_iter() + .map(From::from) + .collect() +} + pub fn empty_buffer_example() -> Buffer { Buffer::new().set_rows(Some(BufferRows::default())) }