1use crate::TestSupportExt as _;
2use std::rc::Rc;
3
4use gpui::{
5 AnyElement, App, Div, ElementId, FocusHandle, InteractiveElement, Interactivity, IntoElement,
6 ParentElement, RenderOnce, Role, StatefulInteractiveElement, StyleRefinement, Styled, Window,
7 div,
8};
9
10use crate::{
11 StyledExt as _,
12 a11y_disabled::A11yDisabledExt as _,
13 actions::{Cancel, Confirm},
14};
15
16type OpenChange = Rc<dyn Fn(bool, &mut Window, &mut App)>;
17
18#[derive(IntoElement)]
21pub struct DatePicker {
22 base: crate::ObservedElement<gpui::Stateful<Div>>,
23 open: bool,
24 disabled: bool,
25 focus_handle: FocusHandle,
26 style: StyleRefinement,
27 children: Vec<AnyElement>,
28 on_open_change: Option<OpenChange>,
29}
30
31impl DatePicker {
32 pub fn new(id: impl Into<ElementId>, focus_handle: &FocusHandle) -> Self {
33 Self {
34 base: div().id(id).test_support(),
35 open: false,
36 disabled: false,
37 focus_handle: focus_handle.clone(),
38 style: StyleRefinement::default(),
39 children: vec![],
40 on_open_change: None,
41 }
42 }
43 pub fn open(mut self, open: bool) -> Self {
44 self.open = open;
45 self
46 }
47 pub fn disabled(mut self, disabled: bool) -> Self {
48 self.disabled = disabled;
49 self
50 }
51 pub fn on_open_change(
52 mut self,
53 handler: impl Fn(bool, &mut Window, &mut App) + 'static,
54 ) -> Self {
55 self.on_open_change = Some(Rc::new(handler));
56 self
57 }
58}
59impl Styled for DatePicker {
60 fn style(&mut self) -> &mut StyleRefinement {
61 &mut self.style
62 }
63}
64impl ParentElement for DatePicker {
65 fn extend(&mut self, children: impl IntoIterator<Item = AnyElement>) {
66 self.children.extend(children);
67 }
68}
69impl InteractiveElement for DatePicker {
70 fn interactivity(&mut self) -> &mut Interactivity {
71 self.base.interactivity()
72 }
73
74 fn track_focus(mut self, handle: &gpui::FocusHandle) -> Self {
75 self.base = self.base.track_focus(handle);
76 self
77 }
78}
79impl StatefulInteractiveElement for DatePicker {}
80impl RenderOnce for DatePicker {
81 fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement {
82 let open = self.open;
83 let disabled = self.disabled;
84 let handler = self.on_open_change;
85 self.base
86 .role(Role::ComboBox)
87 .aria_expanded(open)
88 .track_focus(&self.focus_handle.tab_stop(!disabled))
89 .on_action({
90 let handler = handler.clone();
91 move |_: &Confirm, window, cx| {
92 if disabled {
95 cx.propagate();
96 } else if let Some(handler) = &handler {
97 handler(!open, window, cx);
98 }
99 }
100 })
101 .on_action(move |_: &Cancel, window, cx| {
102 if open {
103 if let Some(handler) = &handler {
104 handler(false, window, cx);
105 }
106 } else {
107 cx.propagate();
108 }
109 })
110 .refine_style(&self.style)
111 .children(self.children)
112 .a11y_disabled(disabled)
113 }
114}