Skip to main content

ui/components/
page_heading.rs

1use gpui::{AnyElement, FontWeight};
2
3use crate::prelude::*;
4
5/// A top-of-page heading: large bold title, optional muted subtitle, and a
6/// right-aligned actions slot (e.g. `Button`s). `mb_6`, `justify_between`,
7/// `items_center` per Tailwind's Page Heading pattern.
8#[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}