palette.rs

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