1use crate::{h_stack, prelude::*, HighlightedText};
2use gpui::{prelude::*, Div};
3use std::path::PathBuf;
4
5#[derive(Clone)]
6pub struct Symbol(pub Vec<HighlightedText>);
7
8#[derive(Component)]
9pub struct Breadcrumb {
10 path: PathBuf,
11 symbols: Vec<Symbol>,
12}
13
14impl Breadcrumb {
15 pub fn new(path: PathBuf, symbols: Vec<Symbol>) -> Self {
16 Self { path, symbols }
17 }
18
19 fn render_separator<V: 'static>(&self, cx: &WindowContext) -> Div<V> {
20 div()
21 .child(" › ")
22 .text_color(cx.theme().colors().text_muted)
23 }
24
25 fn render<V: 'static>(self, view_state: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
26 let symbols_len = self.symbols.len();
27
28 h_stack()
29 .id("breadcrumb")
30 .px_1()
31 .text_ui_sm()
32 .text_color(cx.theme().colors().text_muted)
33 .rounded_md()
34 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
35 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
36 .child(self.path.clone().to_str().unwrap().to_string())
37 .child(if !self.symbols.is_empty() {
38 self.render_separator(cx)
39 } else {
40 div()
41 })
42 .child(
43 div().flex().children(
44 self.symbols
45 .iter()
46 .enumerate()
47 // TODO: Could use something like `intersperse` here instead.
48 .flat_map(|(ix, symbol)| {
49 let mut items =
50 vec![div().flex().children(symbol.0.iter().map(|segment| {
51 div().child(segment.text.clone()).text_color(segment.color)
52 }))];
53
54 let is_last_segment = ix == symbols_len - 1;
55 if !is_last_segment {
56 items.push(self.render_separator(cx));
57 }
58
59 items
60 })
61 .collect::<Vec<_>>(),
62 ),
63 )
64 }
65}
66
67#[cfg(feature = "stories")]
68pub use stories::*;
69
70#[cfg(feature = "stories")]
71mod stories {
72 use super::*;
73 use crate::Story;
74 use gpui::Render;
75 use std::str::FromStr;
76
77 pub struct BreadcrumbStory;
78
79 impl Render for BreadcrumbStory {
80 type Element = Div<Self>;
81
82 fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
83 Story::container(cx)
84 .child(Story::title_for::<_, Breadcrumb>(cx))
85 .child(Story::label(cx, "Default"))
86 .child(Breadcrumb::new(
87 PathBuf::from_str("crates/ui/src/components/toolbar.rs").unwrap(),
88 vec![
89 Symbol(vec![
90 HighlightedText {
91 text: "impl ".to_string(),
92 color: cx.theme().syntax_color("keyword"),
93 },
94 HighlightedText {
95 text: "BreadcrumbStory".to_string(),
96 color: cx.theme().syntax_color("function"),
97 },
98 ]),
99 Symbol(vec![
100 HighlightedText {
101 text: "fn ".to_string(),
102 color: cx.theme().syntax_color("keyword"),
103 },
104 HighlightedText {
105 text: "render".to_string(),
106 color: cx.theme().syntax_color("function"),
107 },
108 ]),
109 ],
110 ))
111 }
112 }
113}