ragrig_gpui/
side_panel.rs1use gpui_kit::{
4 AnyElement, ElementId, FontWeight, InteractiveElement as _, IntoElement, ParentElement,
5 RenderOnce, SharedString, Styled as _, Window,
6 component::{ActiveTheme as _, h_flex, scroll::ScrollableElement as _, v_flex},
7 div,
8 prelude::FluentBuilder as _,
9 px,
10};
11
12#[derive(IntoElement)]
18pub struct SidePanel {
19 id: SharedString,
20 title: SharedString,
21 subtitle: Option<SharedString>,
22 actions: Vec<AnyElement>,
23 toolbar: Option<AnyElement>,
24 children: Vec<AnyElement>,
25 footer: Option<AnyElement>,
26}
27
28impl SidePanel {
29 pub fn new(id: impl Into<SharedString>, title: impl Into<SharedString>) -> Self {
34 Self {
35 id: id.into(),
36 title: title.into(),
37 subtitle: None,
38 actions: Vec::new(),
39 toolbar: None,
40 children: Vec::new(),
41 footer: None,
42 }
43 }
44
45 pub fn subtitle(mut self, subtitle: impl Into<SharedString>) -> Self {
47 self.subtitle = Some(subtitle.into());
48 self
49 }
50
51 pub fn action(mut self, action: impl IntoElement) -> Self {
53 self.actions.push(action.into_any_element());
54 self
55 }
56
57 pub fn toolbar(mut self, toolbar: impl IntoElement) -> Self {
62 self.toolbar = Some(toolbar.into_any_element());
63 self
64 }
65
66 pub fn footer(mut self, footer: impl IntoElement) -> Self {
68 self.footer = Some(footer.into_any_element());
69 self
70 }
71}
72
73impl ParentElement for SidePanel {
74 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
75 self.children.extend(elements);
76 }
77}
78
79impl RenderOnce for SidePanel {
80 fn render(self, _: &mut Window, cx: &mut gpui_kit::App) -> impl IntoElement {
81 let theme = cx.theme();
82 let body_id = ElementId::Name(format!("{}-body", self.id).into());
83
84 let header = v_flex()
85 .gap_1()
86 .px_3()
87 .py_3()
88 .border_b_1()
89 .border_color(theme.border)
90 .child(
91 h_flex()
92 .justify_between()
93 .gap_2()
94 .child(
95 div()
96 .text_lg()
97 .font_weight(FontWeight::SEMIBOLD)
98 .child(self.title),
99 )
100 .child(h_flex().gap_1().children(self.actions)),
101 )
102 .when_some(self.subtitle, |header, subtitle| {
103 header.child(
104 div()
105 .text_sm()
106 .text_color(theme.muted_foreground)
107 .child(subtitle),
108 )
109 });
110
111 let body = v_flex()
112 .id(body_id)
113 .flex_1()
114 .min_h(px(0.))
115 .gap_2()
116 .px_3()
117 .py_3()
118 .overflow_y_scrollbar()
119 .children(self.children);
120
121 v_flex()
122 .size_full()
123 .bg(theme.background)
124 .child(header)
125 .when_some(self.toolbar, |panel, toolbar| {
126 panel.child(
127 div()
128 .px_3()
129 .py_2()
130 .border_b_1()
131 .border_color(theme.border)
132 .child(toolbar),
133 )
134 })
135 .child(body)
136 .when_some(self.footer, |panel, footer| {
137 panel.child(
138 div()
139 .px_3()
140 .py_2()
141 .border_t_1()
142 .border_color(theme.border)
143 .child(footer),
144 )
145 })
146 }
147}