ui/components/
page_heading.rs1use gpui::{AnyElement, FontWeight};
2
3use crate::prelude::*;
4
5#[derive(IntoElement, RegisterComponent)]
9pub struct PageHeading {
10 title: SharedString,
11 subtitle: Option<SharedString>,
12 actions: Option<AnyElement>,
13}
14
15impl PageHeading {
16 pub fn new(title: impl Into<SharedString>) -> Self {
17 Self {
18 title: title.into(),
19 subtitle: None,
20 actions: None,
21 }
22 }
23
24 pub fn subtitle(mut self, subtitle: impl Into<SharedString>) -> Self {
25 self.subtitle = Some(subtitle.into());
26 self
27 }
28
29 pub fn actions(mut self, actions: impl IntoElement) -> Self {
30 self.actions = Some(actions.into_any_element());
31 self
32 }
33}
34
35impl RenderOnce for PageHeading {
36 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
37 h_flex()
38 .w_full()
39 .items_center()
40 .justify_between()
41 .mb_6()
42 .child(
43 v_flex()
44 .gap_1()
45 .child(
46 div()
47 .text_size(rems_from_px(30.))
48 .font_weight(FontWeight::BOLD)
49 .text_color(semantic::text(cx))
50 .child(self.title),
51 )
52 .children(self.subtitle.map(|subtitle| {
53 div()
54 .text_size(rems_from_px(14.))
55 .text_color(semantic::text_muted(cx))
56 .child(subtitle)
57 })),
58 )
59 .children(self.actions)
60 }
61}
62
63impl Component for PageHeading {
64 fn scope() -> ComponentScope {
65 ComponentScope::Typography
66 }
67
68 fn description() -> Option<&'static str> {
69 Some("A page-level heading with title, optional subtitle, and right-aligned actions.")
70 }
71
72 fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
73 Some(
74 PageHeading::new("Team Members")
75 .subtitle("Manage who has access to this workspace.")
76 .actions(Button::new("page-heading-invite", "Invite member"))
77 .into_any_element(),
78 )
79 }
80}