Skip to main content

gpui_component/time/
date_picker.rs

1use std::rc::Rc;
2
3use chrono::{NaiveDate, NaiveDateTime, NaiveTime, Weekday};
4use gpui::{
5    App, AppContext, Bounds, ClickEvent, Context, ElementId, Empty, Entity, EventEmitter,
6    FocusHandle, Focusable, InteractiveElement as _, IntoElement, KeyBinding, MouseButton,
7    ParentElement as _, Pixels, Render, RenderOnce, SharedString, StatefulInteractiveElement as _,
8    StyleRefinement, Styled, Subscription, Window, deferred, div, prelude::FluentBuilder as _, px,
9};
10use rust_i18n::t;
11
12use crate::ThemeStyled as _;
13use crate::{
14    ActiveTheme, Disableable, Icon, IconName, Sizable, Size, StyleSized as _, StyledExt as _,
15    actions::{Cancel, Confirm},
16    button::{Button, ButtonVariants as _},
17    h_flex,
18    input::{Delete, clear_button, input_style},
19    v_flex,
20};
21
22use super::calendar::{Calendar, CalendarEvent, CalendarState, Date, Matcher};
23use super::time_field::{
24    HourCycle, TimeField, TimeFieldEvent, TimeFieldState, TimePrecision, tabular_figures,
25};
26use gpui_base::{DatePicker as BaseDatePicker, ElementExt as _};
27
28const CONTEXT: &'static str = "DatePicker";
29pub(crate) fn init(cx: &mut App) {
30    cx.bind_keys([
31        KeyBinding::new("enter", Confirm { secondary: false }, Some(CONTEXT)),
32        KeyBinding::new("escape", Cancel, Some(CONTEXT)),
33        KeyBinding::new("delete", Delete, Some(CONTEXT)),
34        KeyBinding::new("backspace", Delete, Some(CONTEXT)),
35    ])
36}
37
38/// Events emitted by the DatePicker.
39#[derive(Clone)]
40pub enum DatePickerEvent {
41    /// The user changed the value. With a time precision set, this is emitted
42    /// on every edit while the popup stays open.
43    Change(DateTime),
44}
45
46/// The value of a [`DatePicker`]: the selected date or dates combined with
47/// their times of day.
48///
49/// When the picker has no time precision, every time is the picker's
50/// [`DatePickerState::default_time`], `00:00` unless configured.
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum DateTime {
53    Single(Option<NaiveDateTime>),
54    Range(Option<NaiveDateTime>, Option<NaiveDateTime>),
55}
56
57impl From<NaiveDateTime> for DateTime {
58    fn from(value: NaiveDateTime) -> Self {
59        Self::Single(Some(value))
60    }
61}
62
63impl From<(NaiveDateTime, NaiveDateTime)> for DateTime {
64    fn from((start, end): (NaiveDateTime, NaiveDateTime)) -> Self {
65        Self::Range(Some(start), Some(end))
66    }
67}
68
69impl DateTime {
70    pub fn is_some(&self) -> bool {
71        self.date().is_some()
72    }
73
74    pub fn is_complete(&self) -> bool {
75        self.date().is_complete()
76    }
77
78    pub fn start(&self) -> Option<NaiveDateTime> {
79        match self {
80            Self::Single(v) | Self::Range(v, _) => *v,
81        }
82    }
83
84    pub fn end(&self) -> Option<NaiveDateTime> {
85        match self {
86            Self::Range(_, v) => *v,
87            Self::Single(_) => None,
88        }
89    }
90
91    /// The date part of this value.
92    pub fn date(&self) -> Date {
93        match self {
94            Self::Single(v) => Date::Single(v.map(|v| v.date())),
95            Self::Range(a, b) => Date::Range(a.map(|v| v.date()), b.map(|v| v.date())),
96        }
97    }
98
99    /// Format a complete value, joining a range with ` - `.
100    pub fn format(&self, format: &str) -> Option<SharedString> {
101        match self {
102            Self::Single(Some(v)) => Some(v.format(format).to_string().into()),
103            Self::Range(Some(a), Some(b)) => {
104                Some(format!("{} - {}", a.format(format), b.format(format)).into())
105            }
106            _ => None,
107        }
108    }
109}
110
111/// Preset value for DateRangePreset.
112#[derive(Clone)]
113pub enum DateRangePresetValue {
114    Single(NaiveDate),
115    Range(NaiveDate, NaiveDate),
116    DateTime(DateTime),
117}
118
119/// Preset for date range selection.
120#[derive(Clone)]
121pub struct DateRangePreset {
122    label: SharedString,
123    value: DateRangePresetValue,
124}
125
126impl DateRangePreset {
127    /// Creates a new DateRangePreset with a date.
128    pub fn single(label: impl Into<SharedString>, date: NaiveDate) -> Self {
129        DateRangePreset {
130            label: label.into(),
131            value: DateRangePresetValue::Single(date),
132        }
133    }
134    /// Creates a new DateRangePreset with a range of dates.
135    pub fn range(label: impl Into<SharedString>, start: NaiveDate, end: NaiveDate) -> Self {
136        DateRangePreset {
137            label: label.into(),
138            value: DateRangePresetValue::Range(start, end),
139        }
140    }
141    /// Creates a new DateRangePreset with a date and time, or a range of them.
142    ///
143    /// Times are kept as given, even though a range picker edits dates only.
144    pub fn date_time(label: impl Into<SharedString>, value: impl Into<DateTime>) -> Self {
145        DateRangePreset {
146            label: label.into(),
147            value: DateRangePresetValue::DateTime(value.into()),
148        }
149    }
150}
151
152/// Use to store the state of the date picker.
153pub struct DatePickerState {
154    focus_handle: FocusHandle,
155    date: Date,
156    open: bool,
157    calendar: Entity<CalendarState>,
158    date_format: Option<SharedString>,
159    number_of_months: usize,
160    disabled_matcher: Option<Rc<Matcher>>,
161    /// `None` edits dates only.
162    time_precision: Option<TimePrecision>,
163    hour_cycle: HourCycle,
164    default_time: NaiveTime,
165    start_time: NaiveTime,
166    /// The time of a range's end. Only [`DatePickerState::set_date_time`]
167    /// sets it, since a range picker edits dates only.
168    end_time: NaiveTime,
169    time_field: Entity<TimeFieldState>,
170    time_field_pushed: bool,
171    _subscriptions: Vec<Subscription>,
172    /// The first day of the week. Defaults to Sunday.
173    first_day_of_week: Weekday,
174    bounds: Bounds<Pixels>,
175}
176
177impl Focusable for DatePickerState {
178    fn focus_handle(&self, _: &App) -> FocusHandle {
179        self.focus_handle.clone()
180    }
181}
182impl EventEmitter<DatePickerEvent> for DatePickerState {}
183
184impl DatePickerState {
185    /// Create a date state.
186    pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
187        Self::new_with_range(false, window, cx)
188    }
189
190    /// Create a date state with range mode.
191    pub fn range(window: &mut Window, cx: &mut Context<Self>) -> Self {
192        Self::new_with_range(true, window, cx)
193    }
194
195    fn new_with_range(is_range: bool, window: &mut Window, cx: &mut Context<Self>) -> Self {
196        let date = if is_range {
197            Date::Range(None, None)
198        } else {
199            Date::Single(None)
200        };
201
202        let calendar = cx.new(|cx| {
203            let mut this = CalendarState::new(window, cx);
204            this.set_date(date, window, cx);
205            this
206        });
207        let time_field = cx.new(|cx| TimeFieldState::new(window, cx));
208
209        let _subscriptions = vec![
210            cx.subscribe_in(
211                &calendar,
212                window,
213                |this, _, ev: &CalendarEvent, window, cx| match ev {
214                    CalendarEvent::Selected(date) => {
215                        this.select_date(*date, window, cx);
216                    }
217                },
218            ),
219            cx.subscribe_in(
220                &time_field,
221                window,
222                |this, _, ev: &TimeFieldEvent, _, cx| match ev {
223                    TimeFieldEvent::Change(time) => {
224                        this.start_time = *time;
225                        this.emit_change(cx);
226                    }
227                },
228            ),
229        ];
230
231        Self {
232            focus_handle: cx.focus_handle(),
233            date,
234            calendar,
235            open: false,
236            date_format: None,
237            number_of_months: 1,
238            disabled_matcher: None,
239            time_precision: None,
240            hour_cycle: HourCycle::default(),
241            default_time: NaiveTime::MIN,
242            start_time: NaiveTime::MIN,
243            end_time: NaiveTime::MIN,
244            time_field,
245            time_field_pushed: false,
246            _subscriptions,
247            first_day_of_week: Weekday::Sun,
248            bounds: Bounds::default(),
249        }
250    }
251
252    /// Set the format of the value displayed in the picker.
253    ///
254    /// Default: `%Y/%m/%d`, followed by the time in the configured precision
255    /// and hour cycle when the picker edits times, e.g. `%H:%M` or `%I:%M %p`.
256    pub fn date_format(mut self, format: impl Into<SharedString>) -> Self {
257        self.date_format = Some(format.into());
258        self
259    }
260
261    /// Edit the time of day as well as the date, down to `precision`.
262    ///
263    /// Selecting a date then keeps the popup open, and every change to the
264    /// date or time is reported as it happens. Clicking the selected date
265    /// again closes the popup.
266    ///
267    /// A range picker edits dates only; for a range with times, place two
268    /// single pickers side by side.
269    pub fn time_precision(mut self, precision: TimePrecision) -> Self {
270        self.time_precision = Some(precision);
271        self.default_time = precision.truncate(self.default_time);
272        self.start_time = precision.truncate(self.start_time);
273        self.end_time = precision.truncate(self.end_time);
274        self.time_field_pushed = false;
275        self
276    }
277
278    /// Set how the time field counts hours, default: [`HourCycle::H23`].
279    pub fn hour_cycle(mut self, hour_cycle: HourCycle) -> Self {
280        self.hour_cycle = hour_cycle;
281        self.time_field_pushed = false;
282        self
283    }
284
285    /// Set the time given to a date before the user edits it, default: `00:00`.
286    pub fn default_time(mut self, time: NaiveTime) -> Self {
287        let time = self.truncate_time(time);
288        self.default_time = time;
289        self.start_time = time;
290        self.end_time = time;
291        self.time_field_pushed = false;
292        self
293    }
294
295    /// Set the number of months calendar view to display, default is 1.
296    pub fn number_of_months(mut self, number_of_months: usize) -> Self {
297        self.number_of_months = number_of_months;
298        self
299    }
300
301    /// Set the first day of the week.
302    pub fn first_day_of_week(mut self, day: Weekday) -> Self {
303        self.first_day_of_week = day;
304        self
305    }
306
307    /// Get the date part of the value.
308    pub fn date(&self) -> Date {
309        self.date
310    }
311
312    /// Get the value, combining the date with its time of day.
313    pub fn date_time(&self) -> DateTime {
314        match self.date {
315            Date::Single(date) => DateTime::Single(date.map(|d| d.and_time(self.start_time))),
316            Date::Range(start, end) => DateTime::Range(
317                start.map(|d| d.and_time(self.start_time)),
318                end.map(|d| d.and_time(self.end_time)),
319            ),
320        }
321    }
322
323    /// Set the date, keeping the current time of day.
324    pub fn set_date(&mut self, date: impl Into<Date>, window: &mut Window, cx: &mut Context<Self>) {
325        self.update_date(date.into(), false, window, cx);
326    }
327
328    /// Set the date and the time of day.
329    pub fn set_date_time(
330        &mut self,
331        value: impl Into<DateTime>,
332        window: &mut Window,
333        cx: &mut Context<Self>,
334    ) {
335        let value = value.into();
336        let start = value.start().map_or(self.default_time, |v| v.time());
337        let end = value.end().map_or(start, |v| v.time());
338        self.set_times(start, end, window, cx);
339        self.update_date(value.date(), false, window, cx);
340    }
341
342    /// Set the disabled match for the calendar.
343    pub fn disabled_matcher(mut self, disabled: impl Into<Matcher>) -> Self {
344        self.disabled_matcher = Some(Rc::new(disabled.into()));
345        self
346    }
347
348    /// Set the year range for the internal calendar.
349    ///
350    /// Default is 50 years before and after the current year.
351    /// `range` uses a half-open interval `(start, end)` where `end` is exclusive.
352    pub fn set_year_range(&mut self, range: (i32, i32), cx: &mut Context<Self>) {
353        self.calendar.update(cx, |state, cx| {
354            state.set_year_range(range, cx);
355        });
356    }
357
358    /// The precision the popup edits times at, or `None` when it edits dates only.
359    fn edited_time_precision(&self) -> Option<TimePrecision> {
360        self.time_precision.filter(|_| self.date.is_single())
361    }
362
363    fn truncate_time(&self, time: NaiveTime) -> NaiveTime {
364        self.time_precision
365            .map_or(time, |precision| precision.truncate(time))
366    }
367
368    fn set_times(
369        &mut self,
370        start: NaiveTime,
371        end: NaiveTime,
372        window: &mut Window,
373        cx: &mut Context<Self>,
374    ) {
375        self.start_time = self.truncate_time(start);
376        self.end_time = self.truncate_time(end);
377        self.push_time_field(window, cx);
378    }
379
380    /// Push the configuration and the time into the time field.
381    ///
382    /// User edits flow the other way, through the field subscription, so this
383    /// runs only when the picker's own time changes. Pushing on every render
384    /// could overwrite an edit whose event has not been delivered yet.
385    fn push_time_field(&mut self, window: &mut Window, cx: &mut Context<Self>) {
386        let precision = self.time_precision.unwrap_or_default();
387        let (hour_cycle, time) = (self.hour_cycle, self.start_time);
388        self.time_field.update(cx, |field, cx| {
389            field.set_precision(precision, window, cx);
390            field.set_hour_cycle(hour_cycle, window, cx);
391            field.set_time(time, window, cx);
392        });
393        self.time_field_pushed = true;
394    }
395
396    fn emit_change(&mut self, cx: &mut Context<Self>) {
397        if self.date.is_complete() {
398            cx.emit(DatePickerEvent::Change(self.date_time()));
399        }
400        cx.notify();
401    }
402
403    fn select_date(&mut self, date: Date, window: &mut Window, cx: &mut Context<Self>) {
404        if self.edited_time_precision().is_some() {
405            if date == self.date {
406                // Clicking the selected day again confirms it, so picking a
407                // date and closing is a double-click.
408                self.set_open(false, window, cx);
409                return;
410            }
411            // Keep the popup open so the time can be adjusted next.
412            self.date = date;
413            self.emit_change(cx);
414        } else {
415            self.update_date(date, true, window, cx);
416            self.focus_handle.focus(window, cx);
417        }
418    }
419
420    fn update_date(&mut self, date: Date, emit: bool, window: &mut Window, cx: &mut Context<Self>) {
421        self.date = date;
422        self.calendar.update(cx, |view, cx| {
423            view.set_date(date, window, cx);
424        });
425        self.open = false;
426        if emit {
427            cx.emit(DatePickerEvent::Change(self.date_time()));
428        }
429        cx.notify();
430    }
431
432    /// Sync the builder configuration into the child states before they render.
433    fn sync_children(&mut self, window: &mut Window, cx: &mut Context<Self>) {
434        let matcher = self.disabled_matcher.clone();
435        self.calendar.update(cx, |state, _| {
436            state.set_disabled_matcher_shared(matcher);
437        });
438        // Builders cannot reach the field, so apply them before the first render.
439        if !self.time_field_pushed {
440            self.push_time_field(window, cx);
441        }
442    }
443
444    fn set_open(&mut self, open: bool, window: &mut Window, cx: &mut Context<Self>) {
445        if !open {
446            self.focus_back_if_need(window, cx);
447        }
448        self.open = open;
449        cx.notify();
450    }
451
452    fn on_escape(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
453        if !self.open {
454            cx.propagate();
455        }
456        self.set_open(false, window, cx);
457    }
458
459    fn on_delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
460        self.clean(&ClickEvent::default(), window, cx);
461    }
462
463    // To focus the Picker Input, if current focus in is on the container, or
464    // inside the popup (e.g.: the time field).
465    //
466    // This is because mouse down out the Calendar, GPUI will move focus to the container.
467    // So we need to move focus back to the Picker Input.
468    //
469    // But if mouse down target is some other focusable element (e.g.: [`crate::Input`]), we should not move focus.
470    fn focus_back_if_need(&mut self, window: &mut Window, cx: &mut Context<Self>) {
471        if !self.open {
472            return;
473        }
474
475        if let Some(focused) = window.focused(cx) {
476            if focused.contains(&self.focus_handle, window)
477                || self.focus_handle.contains_focused(window, cx)
478            {
479                self.focus_handle.focus(window, cx);
480            }
481        }
482    }
483
484    fn clean(&mut self, _: &gpui::ClickEvent, window: &mut Window, cx: &mut Context<Self>) {
485        cx.stop_propagation();
486        let default_time = self.default_time;
487        self.set_times(default_time, default_time, window, cx);
488        match self.date {
489            Date::Single(_) => {
490                self.update_date(Date::Single(None), true, window, cx);
491            }
492            Date::Range(_, _) => {
493                self.update_date(Date::Range(None, None), true, window, cx);
494            }
495        }
496    }
497
498    fn toggle_calendar(
499        &mut self,
500        _: &gpui::ClickEvent,
501        window: &mut Window,
502        cx: &mut Context<Self>,
503    ) {
504        let open = !self.open;
505        self.set_open(open, window, cx);
506    }
507
508    fn select_preset(
509        &mut self,
510        preset: &DateRangePreset,
511        window: &mut Window,
512        cx: &mut Context<Self>,
513    ) {
514        match preset.value {
515            DateRangePresetValue::Single(single) => {
516                self.update_date(Date::Single(Some(single)), true, window, cx)
517            }
518            DateRangePresetValue::Range(start, end) => {
519                self.update_date(Date::Range(Some(start), Some(end)), true, window, cx)
520            }
521            DateRangePresetValue::DateTime(value) => {
522                self.set_date_time(value, window, cx);
523                cx.emit(DatePickerEvent::Change(self.date_time()));
524            }
525        }
526        self.focus_handle.focus(window, cx);
527    }
528
529    fn display_format(&self) -> SharedString {
530        if let Some(format) = &self.date_format {
531            return format.clone();
532        }
533        match self.edited_time_precision() {
534            Some(precision) => format!("%Y/%m/%d {}", precision.format(self.hour_cycle)).into(),
535            None => "%Y/%m/%d".into(),
536        }
537    }
538}
539
540/// A DatePicker element.
541#[derive(IntoElement)]
542pub struct DatePicker {
543    id: ElementId,
544    style: StyleRefinement,
545    state: Entity<DatePickerState>,
546    cleanable: bool,
547    placeholder: Option<SharedString>,
548    size: Size,
549    number_of_months: usize,
550    presets: Option<Vec<DateRangePreset>>,
551    appearance: bool,
552    focus_ring_enabled: bool,
553    disabled: bool,
554}
555
556impl Sizable for DatePicker {
557    fn with_size(mut self, size: impl Into<Size>) -> Self {
558        self.size = size.into();
559        self
560    }
561}
562impl Focusable for DatePicker {
563    fn focus_handle(&self, cx: &App) -> FocusHandle {
564        self.state.focus_handle(cx)
565    }
566}
567
568impl Styled for DatePicker {
569    fn style(&mut self) -> &mut StyleRefinement {
570        &mut self.style
571    }
572}
573
574impl Disableable for DatePicker {
575    fn disabled(mut self, disabled: bool) -> Self {
576        self.disabled = disabled;
577        self
578    }
579}
580
581impl crate::FocusableExt for DatePicker {
582    fn focus_ring(mut self, enabled: bool) -> Self {
583        self.focus_ring_enabled = enabled;
584        self
585    }
586
587    fn is_focus_ring_enabled(&self) -> bool {
588        self.focus_ring_enabled
589    }
590}
591
592impl Render for DatePickerState {
593    fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl gpui::IntoElement {
594        Empty
595    }
596}
597
598impl DatePicker {
599    /// Create a new DatePicker with the given [`DatePickerState`].
600    pub fn new(state: &Entity<DatePickerState>) -> Self {
601        Self {
602            id: ("date-picker", state.entity_id()).into(),
603            state: state.clone(),
604            cleanable: false,
605            placeholder: None,
606            size: Size::default(),
607            style: StyleRefinement::default(),
608            number_of_months: 1,
609            presets: None,
610            appearance: true,
611            focus_ring_enabled: true,
612            disabled: false,
613        }
614    }
615
616    /// Set the placeholder of the date picker, default: "".
617    pub fn placeholder(mut self, placeholder: impl Into<SharedString>) -> Self {
618        self.placeholder = Some(placeholder.into());
619        self
620    }
621
622    /// Set whether to show the clear button when the input field is not empty, default is false.
623    pub fn cleanable(mut self, cleanable: bool) -> Self {
624        self.cleanable = cleanable;
625        self
626    }
627
628    /// Set preset ranges for the date picker.
629    pub fn presets(mut self, presets: Vec<DateRangePreset>) -> Self {
630        self.presets = Some(presets);
631        self
632    }
633
634    /// Set number of months to display in the calendar, default is 1.
635    pub fn number_of_months(mut self, number_of_months: usize) -> Self {
636        self.number_of_months = number_of_months;
637        self
638    }
639
640    /// Set appearance of the date picker, if false, the date picker will be in a minimal style.
641    pub fn appearance(mut self, appearance: bool) -> Self {
642        self.appearance = appearance;
643        self
644    }
645
646    fn render_time_field(size: Size, state: &DatePickerState, cx: &App) -> impl IntoElement {
647        h_flex()
648            .map(|this| match size {
649                Size::Small => this.mt_2().pt_2(),
650                _ => this.mt_3().pt_3(),
651            })
652            .gap_3()
653            .justify_between()
654            .border_t_1()
655            .border_color(cx.theme().border)
656            .child(
657                div()
658                    .map(|this| match size {
659                        Size::Small => this.text_xs(),
660                        _ => this.text_sm(),
661                    })
662                    .text_color(cx.theme().muted_foreground)
663                    .child(SharedString::from(t!("DatePicker.time"))),
664            )
665            .child(
666                TimeField::new(&state.time_field)
667                    .with_id("time")
668                    .with_size(size),
669            )
670    }
671}
672
673impl RenderOnce for DatePicker {
674    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
675        self.state.update(cx, |state, cx| {
676            state.sync_children(window, cx);
677        });
678        let month_count = self.number_of_months.max(1) as f32;
679
680        // This for keep focus border style, when click on the popup.
681        let is_focused = self.focus_handle(cx).contains_focused(window, cx);
682        let state = self.state.read(cx);
683        let show_clean = self.cleanable && state.date.is_some();
684        let placeholder = self
685            .placeholder
686            .clone()
687            .unwrap_or_else(|| t!("DatePicker.placeholder").into());
688        let display_title = state
689            .date_time()
690            .format(&state.display_format())
691            .unwrap_or(placeholder.clone());
692
693        let (bg, fg) = input_style(self.disabled, cx);
694
695        let picker_state = self.state.clone();
696
697        BaseDatePicker::new(self.id, &state.focus_handle)
698            .open(state.open)
699            .when(state.date.is_some(), |this| {
700                this.aria_value(display_title.clone())
701            })
702            .disabled(self.disabled)
703            .on_open_change(move |open, window, cx| {
704                picker_state.update(cx, |state, cx| state.set_open(open, window, cx));
705            })
706            .key_context(CONTEXT)
707            .on_action(window.listener_for(&self.state, DatePickerState::on_delete))
708            .flex_none()
709            .w_full()
710            .relative()
711            .on_prepaint({
712                let state = self.state.clone();
713                move |bounds, _, cx| state.update(cx, |state, _| state.bounds = bounds)
714            })
715            .input_text_size(self.size)
716            .refine_style(&self.style)
717            .child(
718                div()
719                    .id("date-picker-input")
720                    .relative()
721                    .flex()
722                    .items_center()
723                    .justify_between()
724                    .when(self.appearance, |this| {
725                        this.bg(bg)
726                            .text_color(fg)
727                            .when(self.disabled, |this| this.opacity(0.5))
728                            .border_1()
729                            .border_color(cx.theme().input)
730                            .rounded(cx.theme().radius)
731                            .when(is_focused, |this| {
732                                this.border_1().border_color(cx.theme().ring)
733                            })
734                    })
735                    .when(
736                        is_focused && self.appearance && !self.disabled && self.focus_ring_enabled,
737                        |this| this.focus_ring_style(window, cx),
738                    )
739                    .input_text_size(self.size)
740                    .input_size(self.size)
741                    .when(!state.open && !self.disabled, |this| {
742                        this.on_click(
743                            window.listener_for(&self.state, DatePickerState::toggle_calendar),
744                        )
745                    })
746                    .child(
747                        h_flex()
748                            .w_full()
749                            .min_w_0()
750                            .overflow_hidden()
751                            .whitespace_nowrap()
752                            .items_center()
753                            .justify_between()
754                            .gap_1()
755                            .child(
756                                div()
757                                    .flex_1()
758                                    .min_w_0()
759                                    .overflow_hidden()
760                                    .whitespace_nowrap()
761                                    .truncate()
762                                    // The value updates live while its time is typed.
763                                    .when(state.edited_time_precision().is_some(), |this| {
764                                        this.font_features(tabular_figures())
765                                    })
766                                    .when(!state.date.is_some(), |this| {
767                                        this.text_color(cx.theme().muted_foreground)
768                                    })
769                                    .child(display_title),
770                            )
771                            .when(!self.disabled, |this| {
772                                this.when(show_clean, |this| {
773                                    this.child(clear_button(cx).on_click(
774                                        window.listener_for(&self.state, DatePickerState::clean),
775                                    ))
776                                })
777                                .when(!show_clean, |this| {
778                                    this.child(
779                                        Icon::new(IconName::Calendar)
780                                            .xsmall()
781                                            .text_color(cx.theme().muted_foreground),
782                                    )
783                                })
784                            }),
785                    ),
786            )
787            .when(state.open, |this| {
788                this.child(
789                    deferred(crate::popover::dropdown_popup(
790                        ("date-picker-popup", self.state.entity_id()),
791                        state.bounds,
792                        div()
793                            .occlude()
794                            .p_3()
795                            .popover_style(cx)
796                            .on_mouse_up_out(
797                                MouseButton::Left,
798                                window.listener_for(&self.state, |view, _, window, cx| {
799                                    view.on_escape(&Cancel, window, cx);
800                                }),
801                            )
802                            .child(
803                                h_flex()
804                                    .gap_3()
805                                    .h_full()
806                                    .items_start()
807                                    .when_some(self.presets.clone(), |this, presets| {
808                                        this.child(v_flex().my_1().gap_2().justify_end().children(
809                                            presets.into_iter().enumerate().map(|(i, preset)| {
810                                                Button::new(("preset", i))
811                                                    .small()
812                                                    .ghost()
813                                                    .tab_stop(false)
814                                                    .label(preset.label.clone())
815                                                    .on_click(window.listener_for(
816                                                        &self.state,
817                                                        move |this, _, window, cx| {
818                                                            this.select_preset(&preset, window, cx);
819                                                        },
820                                                    ))
821                                            }),
822                                        ))
823                                    })
824                                    .child(
825                                        v_flex()
826                                            .child(
827                                                Calendar::new(&state.calendar)
828                                                    .number_of_months(self.number_of_months)
829                                                    .first_day_of_week(state.first_day_of_week)
830                                                    .border_0()
831                                                    .rounded_none()
832                                                    .p_0()
833                                                    .map(|this| match self.size {
834                                                        Size::Small => {
835                                                            this.w(px(196.) * month_count)
836                                                        }
837                                                        Size::Large => {
838                                                            this.w(px(280.) * month_count)
839                                                        }
840                                                        _ => this.w(px(224.) * month_count),
841                                                    })
842                                                    .with_size(self.size),
843                                            )
844                                            .when_some(state.edited_time_precision(), |this, _| {
845                                                this.child(Self::render_time_field(
846                                                    self.size, state, cx,
847                                                ))
848                                            }),
849                                    ),
850                            ),
851                        cx,
852                    ))
853                    .with_priority(gpui_base::POPUP_PRIORITY),
854                )
855            })
856    }
857}