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