
This PR makes the `border` methods require an explicit width instead of defaulting to 1px. This breaks convention with Tailwind, but it makes GPUI more consistent with itself. We already have an edge case where the parameterized method had to be named `border_width`, since `border` was taken up by an alias for the 1px variant. ### Before ```rs div() .border() .border_t() .border_r() .border_b() .border_l() .border_width(px(7.)) ``` ### After ```rs div() .border_1() .border_t_1() .border_r_1() .border_b_1() .border_l_1() .border(px(7.)) ``` Release Notes: - N/A
152 lines
5.4 KiB
Rust
152 lines
5.4 KiB
Rust
use crate::{prelude::*, ButtonLike};
|
|
use smallvec::SmallVec;
|
|
|
|
use gpui::*;
|
|
|
|
#[derive(Default, Clone, Copy, Debug, PartialEq)]
|
|
pub enum ContainerStyle {
|
|
#[default]
|
|
None,
|
|
Card,
|
|
}
|
|
|
|
struct ContainerStyles {
|
|
pub background_color: Hsla,
|
|
pub border_color: Hsla,
|
|
pub text_color: Hsla,
|
|
}
|
|
|
|
#[derive(IntoElement)]
|
|
pub struct CollapsibleContainer {
|
|
id: ElementId,
|
|
base: ButtonLike,
|
|
toggle: bool,
|
|
/// A slot for content that appears before the label, like an icon or avatar.
|
|
start_slot: Option<AnyElement>,
|
|
/// A slot for content that appears after the label, usually on the other side of the header.
|
|
/// This might be a button, a disclosure arrow, a face pile, etc.
|
|
end_slot: Option<AnyElement>,
|
|
style: ContainerStyle,
|
|
children: SmallVec<[AnyElement; 1]>,
|
|
}
|
|
|
|
impl CollapsibleContainer {
|
|
pub fn new(id: impl Into<ElementId>, toggle: bool) -> Self {
|
|
Self {
|
|
id: id.into(),
|
|
base: ButtonLike::new("button_base"),
|
|
toggle,
|
|
start_slot: None,
|
|
end_slot: None,
|
|
style: ContainerStyle::Card,
|
|
children: SmallVec::new(),
|
|
}
|
|
}
|
|
|
|
pub fn start_slot<E: IntoElement>(mut self, start_slot: impl Into<Option<E>>) -> Self {
|
|
self.start_slot = start_slot.into().map(IntoElement::into_any_element);
|
|
self
|
|
}
|
|
|
|
pub fn end_slot<E: IntoElement>(mut self, end_slot: impl Into<Option<E>>) -> Self {
|
|
self.end_slot = end_slot.into().map(IntoElement::into_any_element);
|
|
self
|
|
}
|
|
|
|
pub fn child<E: IntoElement>(mut self, child: E) -> Self {
|
|
self.children.push(child.into_any_element());
|
|
self
|
|
}
|
|
}
|
|
|
|
impl Clickable for CollapsibleContainer {
|
|
fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
|
|
self.base = self.base.on_click(handler);
|
|
self
|
|
}
|
|
}
|
|
|
|
impl RenderOnce for CollapsibleContainer {
|
|
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
|
let color = cx.theme().colors();
|
|
|
|
let styles = match self.style {
|
|
ContainerStyle::None => ContainerStyles {
|
|
background_color: color.ghost_element_background,
|
|
border_color: color.border_transparent,
|
|
text_color: color.text,
|
|
},
|
|
ContainerStyle::Card => ContainerStyles {
|
|
background_color: color.elevated_surface_background,
|
|
border_color: color.border,
|
|
text_color: color.text,
|
|
},
|
|
};
|
|
|
|
v_flex()
|
|
.id(self.id)
|
|
.relative()
|
|
.rounded_md()
|
|
.bg(styles.background_color)
|
|
.border_1()
|
|
.border_color(styles.border_color)
|
|
.text_color(styles.text_color)
|
|
.overflow_hidden()
|
|
.child(
|
|
h_flex()
|
|
.overflow_hidden()
|
|
.w_full()
|
|
.group("toggleable_container_header")
|
|
.border_b_1()
|
|
.border_color(if self.toggle {
|
|
styles.border_color
|
|
} else {
|
|
color.border_transparent
|
|
})
|
|
.child(
|
|
self.base.full_width().style(ButtonStyle::Subtle).child(
|
|
div()
|
|
.h_7()
|
|
.p_1()
|
|
.flex()
|
|
.flex_1()
|
|
.items_center()
|
|
.justify_between()
|
|
.w_full()
|
|
.gap_1()
|
|
.cursor_pointer()
|
|
.group_hover("toggleable_container_header", |this| {
|
|
this.bg(color.element_hover)
|
|
})
|
|
.child(
|
|
h_flex()
|
|
.gap_1()
|
|
.child(
|
|
IconButton::new(
|
|
"toggle_icon",
|
|
match self.toggle {
|
|
true => IconName::ChevronDown,
|
|
false => IconName::ChevronRight,
|
|
},
|
|
)
|
|
.icon_color(Color::Muted)
|
|
.icon_size(IconSize::XSmall),
|
|
)
|
|
.child(
|
|
div()
|
|
.id("label_container")
|
|
.flex()
|
|
.gap_1()
|
|
.items_center()
|
|
.children(self.start_slot),
|
|
),
|
|
)
|
|
.child(h_flex().children(self.end_slot)),
|
|
),
|
|
),
|
|
)
|
|
.when(self.toggle, |this| {
|
|
this.child(h_flex().flex_1().w_full().p_1().children(self.children))
|
|
})
|
|
}
|
|
}
|