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