Skip to main content

guise/
blockquote.rs

1//! `Blockquote` — a quoted passage behind a left accent border, with an
2//! optional icon and citation.
3//!
4//! ```ignore
5//! Blockquote::new()
6//!     .icon(IconName::Info)
7//!     .text("Life is like an npm install — you never know what you are going to get.")
8//!     .cite("– Forrest Gump")
9//! ```
10
11use gpui::prelude::*;
12use gpui::{div, px, AnyElement, App, IntoElement, SharedString, Window};
13
14use crate::icon::{Icon, IconName};
15use crate::theme::{theme, ColorName, Size};
16
17/// A quote block. The Mantine `Blockquote`.
18///
19/// Content is either [`Blockquote::text`], `ParentElement` children
20/// (`.child(..)`), or both — text renders first.
21#[derive(IntoElement)]
22pub struct Blockquote {
23    children: Vec<AnyElement>,
24    text: Option<SharedString>,
25    color: ColorName,
26    cite: Option<SharedString>,
27    icon: Option<IconName>,
28    padding: Size,
29    radius: Option<Size>,
30}
31
32impl Blockquote {
33    pub fn new() -> Self {
34        Blockquote {
35            children: Vec::new(),
36            text: None,
37            color: ColorName::Blue,
38            cite: None,
39            icon: None,
40            padding: Size::Lg,
41            radius: None,
42        }
43    }
44
45    /// The quoted text (shorthand for a single themed text child).
46    pub fn text(mut self, text: impl Into<SharedString>) -> Self {
47        self.text = Some(text.into());
48        self
49    }
50
51    /// The accent color for the border, icon, and background wash.
52    pub fn color(mut self, color: ColorName) -> Self {
53        self.color = color;
54        self
55    }
56
57    /// Attribution line, rendered dimmed below the quote (include your own
58    /// dash, e.g. `"– Forrest Gump"`).
59    pub fn cite(mut self, cite: impl Into<SharedString>) -> Self {
60        self.cite = Some(cite.into());
61        self
62    }
63
64    /// A glyph shown above the quote in the accent color.
65    pub fn icon(mut self, icon: IconName) -> Self {
66        self.icon = Some(icon);
67        self
68    }
69
70    pub fn padding(mut self, padding: Size) -> Self {
71        self.padding = padding;
72        self
73    }
74
75    pub fn radius(mut self, radius: Size) -> Self {
76        self.radius = Some(radius);
77        self
78    }
79}
80
81impl Default for Blockquote {
82    fn default() -> Self {
83        Blockquote::new()
84    }
85}
86
87impl ParentElement for Blockquote {
88    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
89        self.children.extend(elements);
90    }
91}
92
93impl RenderOnce for Blockquote {
94    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
95        let t = theme(cx);
96        let dark = t.scheme.is_dark();
97        let accent = t.color(self.color, if dark { 4 } else { 6 }).hsla();
98        let wash = t.color(self.color, if dark { 5 } else { 6 }).alpha(0.06);
99        let text_color = t.text().hsla();
100        let dimmed = t.dimmed().hsla();
101        let padding = t.spacing(self.padding);
102        let gap = t.spacing(Size::Sm);
103        let radius = t.radius(self.radius.unwrap_or(t.default_radius));
104        let font_md = t.font_size(Size::Md);
105        let font_sm = t.font_size(Size::Sm);
106
107        let mut el = div()
108            .flex()
109            .flex_col()
110            .gap(px(gap))
111            .p(px(padding))
112            .border_l(px(3.0))
113            .border_color(accent)
114            .rounded_r(px(radius))
115            .bg(wash)
116            .text_size(px(font_md))
117            .text_color(text_color);
118
119        if let Some(icon) = self.icon {
120            el = el.child(
121                div()
122                    .flex()
123                    .text_color(accent)
124                    .child(Icon::new(icon).size(Size::Sm)),
125            );
126        }
127        if let Some(text) = self.text {
128            el = el.child(div().child(text));
129        }
130        el = el.children(self.children);
131        if let Some(cite) = self.cite {
132            el = el.child(div().text_size(px(font_sm)).text_color(dimmed).child(cite));
133        }
134        el
135    }
136}