1use std::marker::PhantomData;
2
3use crate::prelude::OrderMethod;
4use crate::theme::theme;
5use crate::{label, palette_item, LabelColor, PaletteItem};
6use gpui2::elements::div::ScrollState;
7use gpui2::style::{StyleHelpers, Styleable};
8use gpui2::{elements::div, IntoElement};
9use gpui2::{Element, ParentElement, ViewContext};
10
11#[derive(Element)]
12pub struct Palette<V: 'static> {
13 view_type: PhantomData<V>,
14 scroll_state: ScrollState,
15 input_placeholder: &'static str,
16 empty_string: &'static str,
17 items: Vec<PaletteItem>,
18 default_order: OrderMethod,
19}
20
21pub fn palette<V: 'static>(scroll_state: ScrollState) -> Palette<V> {
22 Palette {
23 view_type: PhantomData,
24 scroll_state,
25 input_placeholder: "Find something...",
26 empty_string: "No items found.",
27 items: vec![],
28 default_order: OrderMethod::default(),
29 }
30}
31
32impl<V: 'static> Palette<V> {
33 pub fn items(mut self, mut items: Vec<PaletteItem>) -> Self {
34 items.sort_by_key(|item| item.label);
35 self.items = items;
36 self
37 }
38
39 pub fn placeholder(mut self, input_placeholder: &'static str) -> Self {
40 self.input_placeholder = input_placeholder;
41 self
42 }
43
44 pub fn empty_string(mut self, empty_string: &'static str) -> Self {
45 self.empty_string = empty_string;
46 self
47 }
48
49 // TODO: Hook up sort order
50 pub fn default_order(mut self, default_order: OrderMethod) -> Self {
51 self.default_order = default_order;
52 self
53 }
54
55 fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
56 let theme = theme(cx);
57
58 div()
59 .w_96()
60 .rounded_lg()
61 .fill(theme.lowest.base.default.background)
62 .border()
63 .border_color(theme.lowest.base.default.border)
64 .flex()
65 .flex_col()
66 .child(
67 div()
68 .flex()
69 .flex_col()
70 .gap_px()
71 .child(
72 div().py_0p5().px_1().flex().flex_col().child(
73 div().px_2().py_0p5().child(
74 label(self.input_placeholder).color(LabelColor::Placeholder),
75 ),
76 ),
77 )
78 .child(div().h_px().w_full().fill(theme.lowest.base.default.border))
79 .child(
80 div()
81 .py_0p5()
82 .px_1()
83 .flex()
84 .flex_col()
85 .grow()
86 .max_h_96()
87 .overflow_y_scroll(self.scroll_state.clone())
88 .children(
89 vec![if self.items.is_empty() {
90 Some(
91 div()
92 .flex()
93 .flex_row()
94 .justify_between()
95 .px_2()
96 .py_1()
97 .child(
98 label(self.empty_string).color(LabelColor::Muted),
99 ),
100 )
101 } else {
102 None
103 }]
104 .into_iter()
105 .flatten(),
106 )
107 .children(self.items.iter().map(|item| {
108 div()
109 .flex()
110 .flex_row()
111 .justify_between()
112 .px_2()
113 .py_0p5()
114 .rounded_lg()
115 .hover()
116 .fill(theme.lowest.base.hovered.background)
117 .active()
118 .fill(theme.lowest.base.pressed.background)
119 .child(palette_item(item.label, item.keybinding))
120 })),
121 ),
122 )
123 }
124}