Add the Preset Ranges and add border for today (#365)
Preset Ranges have been added. The “Today” border has also been added.  --------- Co-authored-by: Jason Lee <huacnlee@gmail.com>
This commit is contained in:
parent
a798bd81c6
commit
8dbf1dd5d5
3 changed files with 135 additions and 10 deletions
|
|
@ -1,10 +1,10 @@
|
||||||
use chrono::Days;
|
use chrono::{Days, Duration, Utc};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
px, IntoElement, ParentElement as _, Render, Styled as _, View, ViewContext,
|
px, IntoElement, ParentElement as _, Render, Styled as _, View, ViewContext,
|
||||||
VisualContext as _, WindowContext,
|
VisualContext as _, WindowContext,
|
||||||
};
|
};
|
||||||
use ui::{
|
use ui::{
|
||||||
date_picker::{DatePicker, DatePickerEvent},
|
date_picker::{DatePicker, DatePickerEvent, DateRangePreset},
|
||||||
v_flex, Sizable as _,
|
v_flex, Sizable as _,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -37,11 +37,48 @@ impl CalendarStory {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(cx: &mut ViewContext<Self>) -> Self {
|
fn new(cx: &mut ViewContext<Self>) -> Self {
|
||||||
|
let presets = vec![
|
||||||
|
DateRangePreset::single(
|
||||||
|
"Yesterday",
|
||||||
|
(Utc::now() - Duration::days(1)).naive_local().date(),
|
||||||
|
),
|
||||||
|
DateRangePreset::single(
|
||||||
|
"Last Week",
|
||||||
|
(Utc::now() - Duration::weeks(1)).naive_local().date(),
|
||||||
|
),
|
||||||
|
DateRangePreset::single(
|
||||||
|
"Last Month",
|
||||||
|
(Utc::now() - Duration::days(30)).naive_local().date(),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
let range_presets = vec![
|
||||||
|
DateRangePreset::range(
|
||||||
|
"Last 7 Days",
|
||||||
|
(Utc::now() - Duration::days(7)).naive_local().date(),
|
||||||
|
Utc::now().naive_local().date(),
|
||||||
|
),
|
||||||
|
DateRangePreset::range(
|
||||||
|
"Last 14 Days",
|
||||||
|
(Utc::now() - Duration::days(14)).naive_local().date(),
|
||||||
|
Utc::now().naive_local().date(),
|
||||||
|
),
|
||||||
|
DateRangePreset::range(
|
||||||
|
"Last 30 Days",
|
||||||
|
(Utc::now() - Duration::days(30)).naive_local().date(),
|
||||||
|
Utc::now().naive_local().date(),
|
||||||
|
),
|
||||||
|
DateRangePreset::range(
|
||||||
|
"Last 90 Days",
|
||||||
|
(Utc::now() - Duration::days(90)).naive_local().date(),
|
||||||
|
Utc::now().naive_local().date(),
|
||||||
|
),
|
||||||
|
];
|
||||||
let now = chrono::Local::now().naive_local().date();
|
let now = chrono::Local::now().naive_local().date();
|
||||||
let date_picker = cx.new_view(|cx| {
|
let date_picker = cx.new_view(|cx| {
|
||||||
let mut picker = DatePicker::new("date_picker_medium", cx)
|
let mut picker = DatePicker::new("date_picker_medium", cx)
|
||||||
.cleanable()
|
.cleanable()
|
||||||
.width(px(220.));
|
.width(px(220.))
|
||||||
|
.presets(presets);
|
||||||
picker.set_date(now, cx);
|
picker.set_date(now, cx);
|
||||||
picker
|
picker
|
||||||
});
|
});
|
||||||
|
|
@ -62,7 +99,8 @@ impl CalendarStory {
|
||||||
let mut picker = DatePicker::new("date_range_picker", cx)
|
let mut picker = DatePicker::new("date_range_picker", cx)
|
||||||
.width(px(300.))
|
.width(px(300.))
|
||||||
.number_of_months(2)
|
.number_of_months(2)
|
||||||
.cleanable();
|
.cleanable()
|
||||||
|
.presets(range_presets.clone());
|
||||||
picker.set_date((now, now.checked_add_days(Days::new(4)).unwrap()), cx);
|
picker.set_date((now, now.checked_add_days(Days::new(4)).unwrap()), cx);
|
||||||
picker
|
picker
|
||||||
});
|
});
|
||||||
|
|
@ -85,6 +123,7 @@ impl CalendarStory {
|
||||||
.width(px(300.))
|
.width(px(300.))
|
||||||
.placeholder("Range mode picker")
|
.placeholder("Range mode picker")
|
||||||
.cleanable()
|
.cleanable()
|
||||||
|
.presets(range_presets.clone())
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.subscribe(&default_range_mode_picker, |this, _, ev, _| match ev {
|
cx.subscribe(&default_range_mode_picker, |this, _, ev, _| match ev {
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,7 @@ pub struct Calendar {
|
||||||
year_page: i32,
|
year_page: i32,
|
||||||
/// Number of the months view to show.
|
/// Number of the months view to show.
|
||||||
number_of_months: usize,
|
number_of_months: usize,
|
||||||
|
today: NaiveDate,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Calendar {
|
impl Calendar {
|
||||||
|
|
@ -173,6 +174,7 @@ impl Calendar {
|
||||||
years: vec![],
|
years: vec![],
|
||||||
year_page: 0,
|
year_page: 0,
|
||||||
number_of_months: 1,
|
number_of_months: 1,
|
||||||
|
today,
|
||||||
}
|
}
|
||||||
.year_range((today.year() - 50, today.year() + 50))
|
.year_range((today.year() - 50, today.year() + 50))
|
||||||
}
|
}
|
||||||
|
|
@ -399,6 +401,7 @@ impl Calendar {
|
||||||
let is_in_range = self.date.is_in_range(d);
|
let is_in_range = self.date.is_in_range(d);
|
||||||
|
|
||||||
let date = *d;
|
let date = *d;
|
||||||
|
let is_today = *d == self.today;
|
||||||
|
|
||||||
self.item_button(
|
self.item_button(
|
||||||
ix,
|
ix,
|
||||||
|
|
@ -408,6 +411,9 @@ impl Calendar {
|
||||||
!is_current_month,
|
!is_current_month,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
.when(is_today && !is_active, |this| {
|
||||||
|
this.border_1().border_color(cx.theme().border)
|
||||||
|
}) // Add border for today
|
||||||
.on_click(cx.listener(move |view, _: &ClickEvent, cx| {
|
.on_click(cx.listener(move |view, _: &ClickEvent, cx| {
|
||||||
if view.date.is_single() {
|
if view.date.is_single() {
|
||||||
view.set_date(date, cx);
|
view.set_date(date, cx);
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,19 @@
|
||||||
|
use chrono::NaiveDate;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
anchored, deferred, div, prelude::FluentBuilder as _, px, AppContext, ElementId, EventEmitter,
|
anchored, deferred, div, prelude::FluentBuilder as _, px, AppContext, ElementId, EventEmitter,
|
||||||
FocusHandle, FocusableView, InteractiveElement as _, KeyBinding, Length, MouseButton,
|
FocusHandle, FocusableView, InteractiveElement as _, KeyBinding, Length, MouseButton,
|
||||||
ParentElement as _, Render, SharedString, StatefulInteractiveElement as _, Styled as _, View,
|
ParentElement as _, Render, SharedString, StatefulInteractiveElement as _, Styled, View,
|
||||||
ViewContext, VisualContext as _,
|
ViewContext, VisualContext as _,
|
||||||
};
|
};
|
||||||
use rust_i18n::t;
|
use rust_i18n::t;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dropdown::Escape, h_flex, input::ClearButton, theme::ActiveTheme as _, Icon, IconName, Sizable,
|
button::{Button, ButtonStyled as _},
|
||||||
Size, StyleSized as _, StyledExt as _,
|
dropdown::Escape,
|
||||||
|
h_flex,
|
||||||
|
input::ClearButton,
|
||||||
|
theme::ActiveTheme,
|
||||||
|
v_flex, Icon, IconName, Sizable, Size, StyleSized as _, StyledExt as _,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::calendar::{Calendar, CalendarEvent, Date};
|
use super::calendar::{Calendar, CalendarEvent, Date};
|
||||||
|
|
@ -23,6 +28,34 @@ pub enum DatePickerEvent {
|
||||||
Change(Date),
|
Change(Date),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum DateRangePresetValue {
|
||||||
|
Single(NaiveDate),
|
||||||
|
Range(NaiveDate, NaiveDate),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct DateRangePreset {
|
||||||
|
label: SharedString,
|
||||||
|
value: DateRangePresetValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DateRangePreset {
|
||||||
|
/// Creates a new DateRangePreset with single date.
|
||||||
|
pub fn single(label: impl Into<SharedString>, single: NaiveDate) -> Self {
|
||||||
|
DateRangePreset {
|
||||||
|
label: label.into(),
|
||||||
|
value: DateRangePresetValue::Single(single),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Creates a new DateRangePreset with a range of dates.
|
||||||
|
pub fn range(label: impl Into<SharedString>, start: NaiveDate, end: NaiveDate) -> Self {
|
||||||
|
DateRangePreset {
|
||||||
|
label: label.into(),
|
||||||
|
value: DateRangePresetValue::Range(start, end),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
pub struct DatePicker {
|
pub struct DatePicker {
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
|
|
@ -35,6 +68,7 @@ pub struct DatePicker {
|
||||||
date_format: SharedString,
|
date_format: SharedString,
|
||||||
calendar: View<Calendar>,
|
calendar: View<Calendar>,
|
||||||
number_of_months: usize,
|
number_of_months: usize,
|
||||||
|
presets: Option<Vec<DateRangePreset>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DatePicker {
|
impl DatePicker {
|
||||||
|
|
@ -82,6 +116,7 @@ impl DatePicker {
|
||||||
cleanable: false,
|
cleanable: false,
|
||||||
number_of_months: 1,
|
number_of_months: 1,
|
||||||
placeholder: None,
|
placeholder: None,
|
||||||
|
presets: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,6 +150,12 @@ impl DatePicker {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set preset ranges for the date picker.
|
||||||
|
pub fn presets(mut self, presets: Vec<DateRangePreset>) -> Self {
|
||||||
|
self.presets = Some(presets);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the date of the date picker.
|
/// Get the date of the date picker.
|
||||||
pub fn date(&self) -> Date {
|
pub fn date(&self) -> Date {
|
||||||
self.date
|
self.date
|
||||||
|
|
@ -158,6 +199,17 @@ impl DatePicker {
|
||||||
self.open = !self.open;
|
self.open = !self.open;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn select_preset(&mut self, preset: &DateRangePreset, cx: &mut ViewContext<Self>) {
|
||||||
|
match preset.value {
|
||||||
|
DateRangePresetValue::Single(single) => {
|
||||||
|
self.update_date(Date::Single(Some(single)), true, cx)
|
||||||
|
}
|
||||||
|
DateRangePresetValue::Range(start, end) => {
|
||||||
|
self.update_date(Date::Range(Some(start), Some(end)), true, cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventEmitter<DatePickerEvent> for DatePicker {}
|
impl EventEmitter<DatePickerEvent> for DatePicker {}
|
||||||
|
|
@ -190,8 +242,9 @@ impl Render for DatePicker {
|
||||||
view.set_number_of_months(self.number_of_months, cx);
|
view.set_number_of_months(self.number_of_months, cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
let popover_width =
|
let popover_width = self.presets.as_ref().map_or(0.0, |_| 136.0)
|
||||||
285.0 * self.number_of_months as f32 + (self.number_of_months - 1) as f32 * 16.0;
|
+ 285.0 * self.number_of_months as f32
|
||||||
|
+ (self.number_of_months - 1) as f32 * 16.0;
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.id(self.id.clone())
|
.id(self.id.clone())
|
||||||
|
|
@ -266,7 +319,34 @@ impl Render for DatePicker {
|
||||||
MouseButton::Left,
|
MouseButton::Left,
|
||||||
cx.listener(|view, _, cx| view.escape(&Escape, cx)),
|
cx.listener(|view, _, cx| view.escape(&Escape, cx)),
|
||||||
)
|
)
|
||||||
.child(self.calendar.clone()),
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.gap_3()
|
||||||
|
.h_full()
|
||||||
|
.items_start()
|
||||||
|
.when_some(self.presets.clone(), |this, presets| {
|
||||||
|
this.child(
|
||||||
|
v_flex().my_1().gap_2().justify_end().children(
|
||||||
|
presets.into_iter().enumerate().map(
|
||||||
|
|(i, preset)| {
|
||||||
|
Button::new(("preset", i))
|
||||||
|
.small()
|
||||||
|
.ghost()
|
||||||
|
.label(preset.label.clone())
|
||||||
|
.on_click(cx.listener(
|
||||||
|
move |this, _, cx| {
|
||||||
|
this.select_preset(
|
||||||
|
&preset, cx,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
))
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.child(self.calendar.clone()),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.with_priority(2),
|
.with_priority(2),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue