Skip to main content

gpui_base/
date_picker.rs

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/// Unstyled controlled date-picker root. Applications own its trigger, calendar,
18/// positioning, and visual presentation; Base owns focus and open/dismiss keyboard behavior.
19#[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}