From b118e601601973aea4428acc0b0a817c75cc0e0c Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 7 Oct 2023 11:18:06 -0400 Subject: [PATCH] Add `Breadcrumb` component --- crates/storybook2/src/stories/components.rs | 1 + .../src/stories/components/breadcrumb.rs | 54 +++++++++++++ crates/storybook2/src/story_selector.rs | 2 + crates/ui2/src/components.rs | 2 + crates/ui2/src/components/breadcrumb.rs | 77 +++++++++++++++++++ 5 files changed, 136 insertions(+) create mode 100644 crates/storybook2/src/stories/components/breadcrumb.rs create mode 100644 crates/ui2/src/components/breadcrumb.rs diff --git a/crates/storybook2/src/stories/components.rs b/crates/storybook2/src/stories/components.rs index 056896b0f96581829c75d6a44cb3756d43b5b8d4..38a93adec60fa26adb28d0f81fb2c0aaaacb6735 100644 --- a/crates/storybook2/src/stories/components.rs +++ b/crates/storybook2/src/stories/components.rs @@ -1,4 +1,5 @@ pub mod assistant_panel; +pub mod breadcrumb; pub mod buffer; pub mod panel; pub mod project_panel; diff --git a/crates/storybook2/src/stories/components/breadcrumb.rs b/crates/storybook2/src/stories/components/breadcrumb.rs new file mode 100644 index 0000000000000000000000000000000000000000..48eed1f7ef4d053b4b6797a405514a028352e3b9 --- /dev/null +++ b/crates/storybook2/src/stories/components/breadcrumb.rs @@ -0,0 +1,54 @@ +use std::marker::PhantomData; +use std::path::PathBuf; +use std::str::FromStr; + +use ui::prelude::*; +use ui::{Breadcrumb, HighlightedText, Symbol}; + +use crate::story::Story; + +#[derive(Element)] +pub struct BreadcrumbStory { + state_type: PhantomData, +} + +impl BreadcrumbStory { + pub fn new() -> Self { + Self { + state_type: PhantomData, + } + } + + fn render(&mut self, cx: &mut ViewContext) -> impl Element { + let theme = theme(cx); + + Story::container(cx) + .child(Story::title_for::<_, Breadcrumb>(cx)) + .child(Story::label(cx, "Default")) + .child(Breadcrumb::new( + PathBuf::from_str("crates/ui/src/components/toolbar.rs").unwrap(), + vec![ + Symbol(vec![ + HighlightedText { + text: "impl ".to_string(), + color: HighlightColor::Keyword.hsla(&theme), + }, + HighlightedText { + text: "BreadcrumbStory".to_string(), + color: HighlightColor::Function.hsla(&theme), + }, + ]), + Symbol(vec![ + HighlightedText { + text: "fn ".to_string(), + color: HighlightColor::Keyword.hsla(&theme), + }, + HighlightedText { + text: "render".to_string(), + color: HighlightColor::Function.hsla(&theme), + }, + ]), + ], + )) + } +} diff --git a/crates/storybook2/src/story_selector.rs b/crates/storybook2/src/story_selector.rs index 8fcb1e66bf2666bb790d15a87430d315d20a4150..937699c03320e0adff5f8d695a9c3d0bbadf9a2c 100644 --- a/crates/storybook2/src/story_selector.rs +++ b/crates/storybook2/src/story_selector.rs @@ -35,6 +35,7 @@ impl ElementStory { #[strum(serialize_all = "snake_case")] pub enum ComponentStory { AssistantPanel, + Breadcrumb, Buffer, Panel, ProjectPanel, @@ -53,6 +54,7 @@ impl ComponentStory { components::assistant_panel::AssistantPanelStory::new().into_any() } Self::Buffer => components::buffer::BufferStory::new().into_any(), + Self::Breadcrumb => components::breadcrumb::BreadcrumbStory::new().into_any(), Self::Panel => components::panel::PanelStory::new().into_any(), Self::ProjectPanel => components::project_panel::ProjectPanelStory::new().into_any(), Self::Tab => components::tab::TabStory::new().into_any(), diff --git a/crates/ui2/src/components.rs b/crates/ui2/src/components.rs index 15cb304b18e6ac97508c1ca5c139b4ad0f0cb7c6..4ebd0e07698a833b374b2fe57c955a49d572dfb3 100644 --- a/crates/ui2/src/components.rs +++ b/crates/ui2/src/components.rs @@ -1,4 +1,5 @@ mod assistant_panel; +mod breadcrumb; mod buffer; mod icon_button; mod list; @@ -12,6 +13,7 @@ mod terminal; mod workspace; pub use assistant_panel::*; +pub use breadcrumb::*; pub use buffer::*; pub use icon_button::*; pub use list::*; diff --git a/crates/ui2/src/components/breadcrumb.rs b/crates/ui2/src/components/breadcrumb.rs new file mode 100644 index 0000000000000000000000000000000000000000..98046b42643d9931ca4937af67ca3efb6934c610 --- /dev/null +++ b/crates/ui2/src/components/breadcrumb.rs @@ -0,0 +1,77 @@ +use std::marker::PhantomData; +use std::path::PathBuf; + +use gpui3::Div; + +use crate::prelude::*; +use crate::{h_stack, HighlightedText}; + +#[derive(Clone)] +pub struct Symbol(pub Vec); + +#[derive(Element)] +pub struct Breadcrumb { + state_type: PhantomData, + path: PathBuf, + symbols: Vec, +} + +impl Breadcrumb { + pub fn new(path: PathBuf, symbols: Vec) -> Self { + Self { + state_type: PhantomData, + path, + symbols, + } + } + + fn render_separator(&self, theme: &Theme) -> Div { + div() + .child(" › ") + .text_color(HighlightColor::Default.hsla(theme)) + } + + fn render(&mut self, cx: &mut ViewContext) -> impl Element { + let theme = theme(cx); + + let symbols_len = self.symbols.len(); + + h_stack() + .px_1() + // TODO: Read font from theme (or settings?). + .font("Zed Mono Extended") + .text_sm() + .text_color(theme.middle.base.default.foreground) + .rounded_md() + // .hover() + // .fill(theme.highest.base.hovered.background) + .child(self.path.clone().to_str().unwrap().to_string()) + .child(if !self.symbols.is_empty() { + self.render_separator(&theme) + } else { + div() + }) + .child( + div().flex().children( + self.symbols + .iter() + .enumerate() + // TODO: Could use something like `intersperse` here instead. + .flat_map(|(ix, symbol)| { + let mut items = + vec![div().flex().children(symbol.0.iter().map(|segment| { + div().child(segment.text.clone()).text_color(segment.color) + }))]; + + let is_last_segment = ix == symbols_len - 1; + if !is_last_segment { + items.push(self.render_separator(&theme)); + } + + items + }) + .collect::>(), + ), + ) + } +}