Skip to main content

gpui_base/
date_picker.rs

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