list_header.rs

  1use gpui::{AnyElement, ClickEvent, Div, Stateful};
  2
  3use crate::{h_stack, prelude::*, Disclosure, Label};
  4
  5#[derive(IntoElement)]
  6pub struct ListHeader {
  7    /// The label of the header.
  8    label: SharedString,
  9    /// A slot for content that appears before the label, like an icon or avatar.
 10    start_slot: Option<AnyElement>,
 11    /// A slot for content that appears after the label, usually on the other side of the header.
 12    /// This might be a button, a disclosure arrow, a face pile, etc.
 13    end_slot: Option<AnyElement>,
 14    /// A slot for content that appears on hover after the label
 15    /// It will obscure the `end_slot` when visible.
 16    end_hover_slot: Option<AnyElement>,
 17    toggle: Option<bool>,
 18    on_toggle: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
 19    inset: bool,
 20    selected: bool,
 21}
 22
 23impl ListHeader {
 24    pub fn new(label: impl Into<SharedString>) -> Self {
 25        Self {
 26            label: label.into(),
 27            start_slot: None,
 28            end_slot: None,
 29            end_hover_slot: None,
 30            inset: false,
 31            toggle: None,
 32            on_toggle: None,
 33            selected: false,
 34        }
 35    }
 36
 37    pub fn toggle(mut self, toggle: impl Into<Option<bool>>) -> Self {
 38        self.toggle = toggle.into();
 39        self
 40    }
 41
 42    pub fn on_toggle(
 43        mut self,
 44        on_toggle: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
 45    ) -> Self {
 46        self.on_toggle = Some(Box::new(on_toggle));
 47        self
 48    }
 49
 50    pub fn start_slot<E: IntoElement>(mut self, start_slot: impl Into<Option<E>>) -> Self {
 51        self.start_slot = start_slot.into().map(IntoElement::into_any_element);
 52        self
 53    }
 54
 55    pub fn end_slot<E: IntoElement>(mut self, end_slot: impl Into<Option<E>>) -> Self {
 56        self.end_slot = end_slot.into().map(IntoElement::into_any_element);
 57        self
 58    }
 59
 60    pub fn end_hover_slot<E: IntoElement>(mut self, end_hover_slot: impl Into<Option<E>>) -> Self {
 61        self.end_hover_slot = end_hover_slot.into().map(IntoElement::into_any_element);
 62        self
 63    }
 64
 65    pub fn inset(mut self, inset: bool) -> Self {
 66        self.inset = inset;
 67        self
 68    }
 69}
 70
 71impl Selectable for ListHeader {
 72    fn selected(mut self, selected: bool) -> Self {
 73        self.selected = selected;
 74        self
 75    }
 76}
 77
 78impl RenderOnce for ListHeader {
 79    type Rendered = Stateful<Div>;
 80
 81    fn render(self, cx: &mut WindowContext) -> Self::Rendered {
 82        h_stack()
 83            .id(self.label.clone())
 84            .w_full()
 85            .relative()
 86            .group("list_header")
 87            .child(
 88                div()
 89                    .h_7()
 90                    .when(self.inset, |this| this.px_2())
 91                    .when(self.selected, |this| {
 92                        this.bg(cx.theme().colors().ghost_element_selected)
 93                    })
 94                    .flex()
 95                    .flex_1()
 96                    .items_center()
 97                    .justify_between()
 98                    .w_full()
 99                    .gap_1()
100                    .child(
101                        h_stack()
102                            .gap_1()
103                            .children(self.toggle.map(|is_open| {
104                                Disclosure::new("toggle", is_open).on_toggle(self.on_toggle)
105                            }))
106                            .child(
107                                div()
108                                    .flex()
109                                    .gap_1()
110                                    .items_center()
111                                    .children(self.start_slot)
112                                    .child(Label::new(self.label.clone()).color(Color::Muted)),
113                            ),
114                    )
115                    .child(h_stack().children(self.end_slot))
116                    .when_some(self.end_hover_slot, |this, end_hover_slot| {
117                        this.child(
118                            div()
119                                .absolute()
120                                .right_0()
121                                .visible_on_hover("list_header")
122                                .child(end_hover_slot),
123                        )
124                    }),
125            )
126    }
127}