1use crate::motion::StyledSlot;
2use crate::theme::ActiveTheme;
3use gpui::{
4 div, prelude::*, px, App, FocusHandle, FontWeight, IntoElement, RenderOnce, SharedString,
5 StyleRefinement, Styled, Window,
6};
7
8use crate::compat::{AccessibilityExt, Role};
9
10#[derive(IntoElement)]
12pub struct Label {
13 id: SharedString,
14 text: SharedString,
15 required: bool,
16 optional: bool,
17 focus_handle: Option<FocusHandle>,
18 style: StyleRefinement,
19}
20
21impl Label {
22 pub fn new(text: impl Into<SharedString>) -> Self {
23 let text = text.into();
24 Self {
25 id: SharedString::from(format!("label-{text}")),
26 text,
27 required: false,
28 optional: false,
29 focus_handle: None,
30 style: StyleRefinement::default(),
31 }
32 }
33
34 pub fn id(mut self, id: impl Into<SharedString>) -> Self {
35 self.id = id.into();
36 self
37 }
38
39 pub fn required(mut self, required: bool) -> Self {
40 self.required = required;
41 self
42 }
43
44 pub fn optional(mut self, optional: bool) -> Self {
45 self.optional = optional;
46 self
47 }
48
49 pub fn focus_handle(mut self, focus_handle: FocusHandle) -> Self {
51 self.focus_handle = Some(focus_handle);
52 self
53 }
54}
55
56impl Styled for Label {
57 fn style(&mut self) -> &mut StyleRefinement {
58 &mut self.style
59 }
60}
61
62impl RenderOnce for Label {
63 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
64 let theme = cx.theme();
65 let focus_handle = self.focus_handle;
66 div()
67 .id(self.id)
68 .role(Role::Label)
69 .flex()
70 .items_center()
71 .gap(px(4.))
72 .font_family(theme.font_family)
73 .font_weight(FontWeight::MEDIUM)
74 .text_size(px(13.))
75 .line_height(px(16.))
76 .text_color(theme.label)
77 .refine_style(&self.style)
78 .when(focus_handle.is_some(), |el| el.cursor_pointer())
79 .when_some(focus_handle, |el, focus_handle| {
80 el.on_click(move |_, window, _cx| {
81 focus_handle.focus(window);
82 })
83 })
84 .child(self.text)
85 .when(self.required, |el| {
86 el.child(
87 div()
88 .font_family(theme.font_family)
89 .font_weight(FontWeight::MEDIUM)
90 .text_size(px(13.))
91 .line_height(px(16.))
92 .text_color(theme.destructive)
93 .child("*"),
94 )
95 })
96 .when(self.optional, |el| {
97 el.child(
98 div()
99 .font_family(theme.font_family)
100 .font_weight(FontWeight::MEDIUM)
101 .text_size(px(12.))
102 .line_height(px(16.))
103 .text_color(theme.body)
104 .child("Optional"),
105 )
106 })
107 }
108}