palette.rs

  1use crate::{h_stack, prelude::*, v_stack, KeyBinding, Label};
  2use gpui::prelude::*;
  3use gpui::Div;
  4use gpui::Stateful;
  5
  6#[derive(RenderOnce)]
  7pub struct Palette {
  8    id: ElementId,
  9    input_placeholder: SharedString,
 10    empty_string: SharedString,
 11    items: Vec<PaletteItem>,
 12    default_order: OrderMethod,
 13}
 14
 15impl<V: 'static> Component<V> for Palette {
 16    type Rendered = Stateful<V, Div<V>>;
 17
 18    fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
 19        v_stack()
 20            .id(self.id)
 21            .w_96()
 22            .rounded_lg()
 23            .bg(cx.theme().colors().elevated_surface_background)
 24            .border()
 25            .border_color(cx.theme().colors().border)
 26            .child(
 27                v_stack()
 28                    .gap_px()
 29                    .child(v_stack().py_0p5().px_1().child(
 30                        div().px_2().py_0p5().child(
 31                            Label::new(self.input_placeholder).color(TextColor::Placeholder),
 32                        ),
 33                    ))
 34                    .child(
 35                        div()
 36                            .h_px()
 37                            .w_full()
 38                            .bg(cx.theme().colors().element_background),
 39                    )
 40                    .child(
 41                        v_stack()
 42                            .id("items")
 43                            .py_0p5()
 44                            .px_1()
 45                            .grow()
 46                            .max_h_96()
 47                            .overflow_y_scroll()
 48                            .children(
 49                                vec![if self.items.is_empty() {
 50                                    Some(h_stack().justify_between().px_2().py_1().child(
 51                                        Label::new(self.empty_string).color(TextColor::Muted),
 52                                    ))
 53                                } else {
 54                                    None
 55                                }]
 56                                .into_iter()
 57                                .flatten(),
 58                            )
 59                            .children(self.items.into_iter().enumerate().map(|(index, item)| {
 60                                h_stack()
 61                                    .id(index)
 62                                    .justify_between()
 63                                    .px_2()
 64                                    .py_0p5()
 65                                    .rounded_lg()
 66                                    .hover(|style| {
 67                                        style.bg(cx.theme().colors().ghost_element_hover)
 68                                    })
 69                                    .active(|style| {
 70                                        style.bg(cx.theme().colors().ghost_element_active)
 71                                    })
 72                                    .child(item)
 73                            })),
 74                    ),
 75            )
 76    }
 77}
 78
 79impl Palette {
 80    pub fn new(id: impl Into<ElementId>) -> Self {
 81        Self {
 82            id: id.into(),
 83            input_placeholder: "Find something...".into(),
 84            empty_string: "No items found.".into(),
 85            items: vec![],
 86            default_order: OrderMethod::default(),
 87        }
 88    }
 89
 90    pub fn items(mut self, items: Vec<PaletteItem>) -> Self {
 91        self.items = items;
 92        self
 93    }
 94
 95    pub fn placeholder(mut self, input_placeholder: impl Into<SharedString>) -> Self {
 96        self.input_placeholder = input_placeholder.into();
 97        self
 98    }
 99
100    pub fn empty_string(mut self, empty_string: impl Into<SharedString>) -> Self {
101        self.empty_string = empty_string.into();
102        self
103    }
104
105    // TODO: Hook up sort order
106    pub fn default_order(mut self, default_order: OrderMethod) -> Self {
107        self.default_order = default_order;
108        self
109    }
110}
111
112#[derive(RenderOnce)]
113pub struct PaletteItem {
114    pub label: SharedString,
115    pub sublabel: Option<SharedString>,
116    pub key_binding: Option<KeyBinding>,
117}
118
119impl<V: 'static> Component<V> for PaletteItem {
120    type Rendered = Div<V>;
121
122    fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
123        div()
124            .flex()
125            .flex_row()
126            .grow()
127            .justify_between()
128            .child(
129                v_stack()
130                    .child(Label::new(self.label))
131                    .children(self.sublabel.map(|sublabel| Label::new(sublabel))),
132            )
133            .children(self.key_binding)
134    }
135}
136
137impl PaletteItem {
138    pub fn new(label: impl Into<SharedString>) -> Self {
139        Self {
140            label: label.into(),
141            sublabel: None,
142            key_binding: None,
143        }
144    }
145
146    pub fn label(mut self, label: impl Into<SharedString>) -> Self {
147        self.label = label.into();
148        self
149    }
150
151    pub fn sublabel(mut self, sublabel: impl Into<Option<SharedString>>) -> Self {
152        self.sublabel = sublabel.into();
153        self
154    }
155
156    pub fn key_binding(mut self, key_binding: impl Into<Option<KeyBinding>>) -> Self {
157        self.key_binding = key_binding.into();
158        self
159    }
160}
161
162use gpui::ElementId;
163#[cfg(feature = "stories")]
164pub use stories::*;
165
166#[cfg(feature = "stories")]
167mod stories {
168    use gpui::{Div, Render};
169
170    use crate::{binding, Story};
171
172    use super::*;
173
174    pub struct PaletteStory;
175
176    impl Render<Self> for PaletteStory {
177        type Element = Div<Self>;
178
179        fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
180            {
181                Story::container(cx)
182                    .child(Story::title_for::<_, Palette>(cx))
183                    .child(Story::label(cx, "Default"))
184                    .child(Palette::new("palette-1"))
185                    .child(Story::label(cx, "With Items"))
186                    .child(
187                        Palette::new("palette-2")
188                            .placeholder("Execute a command...")
189                            .items(vec![
190                                PaletteItem::new("theme selector: toggle")
191                                    .key_binding(KeyBinding::new(binding("cmd-k cmd-t"))),
192                                PaletteItem::new("assistant: inline assist")
193                                    .key_binding(KeyBinding::new(binding("cmd-enter"))),
194                                PaletteItem::new("assistant: quote selection")
195                                    .key_binding(KeyBinding::new(binding("cmd-<"))),
196                                PaletteItem::new("assistant: toggle focus")
197                                    .key_binding(KeyBinding::new(binding("cmd-?"))),
198                                PaletteItem::new("auto update: check"),
199                                PaletteItem::new("auto update: view release notes"),
200                                PaletteItem::new("branches: open recent")
201                                    .key_binding(KeyBinding::new(binding("cmd-alt-b"))),
202                                PaletteItem::new("chat panel: toggle focus"),
203                                PaletteItem::new("cli: install"),
204                                PaletteItem::new("client: sign in"),
205                                PaletteItem::new("client: sign out"),
206                                PaletteItem::new("editor: cancel")
207                                    .key_binding(KeyBinding::new(binding("escape"))),
208                            ]),
209                    )
210            }
211        }
212    }
213}