diff --git a/crates/story/src/calendar_story.rs b/crates/story/src/calendar_story.rs index f6ec6c36..4cf30819 100644 --- a/crates/story/src/calendar_story.rs +++ b/crates/story/src/calendar_story.rs @@ -1,23 +1,15 @@ -use chrono::{Days, Duration, Utc}; use gpui::{ - px, App, AppContext, Context, Entity, Focusable, IntoElement, ParentElement as _, Render, - Styled as _, Window, -}; -use gpui_component::{ - button::Button, - calendar, - date_picker::{DatePicker, DatePickerEvent, DateRangePreset}, - v_flex, Sizable as _, Size, + App, AppContext, Context, Entity, FocusHandle, Focusable, IntoElement, ParentElement as _, + Render, Styled as _, Window, }; +use gpui_component::{calendar::Calendar, v_flex}; + +use crate::section; pub struct CalendarStory { - size: Size, - date_picker: Entity, - date_picker_small: Entity, - date_picker_large: Entity, - date_picker_value: Option, - date_range_picker: Entity, - default_range_mode_picker: Entity, + focus_handle: FocusHandle, + calendar: Entity, + calendar_wide: Entity, } impl super::Story for CalendarStory { @@ -26,7 +18,7 @@ impl super::Story for CalendarStory { } fn description() -> &'static str { - "A date picker and calendar component." + "A calendar to select a date or date range." } fn new_view(window: &mut Window, cx: &mut App) -> Entity { @@ -40,173 +32,32 @@ impl CalendarStory { } fn new(window: &mut Window, cx: &mut Context) -> 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 date_picker = cx.new(|cx| { - let mut picker = DatePicker::new("date_picker_medium", window, cx) - .cleanable() - .width(px(220.)) - .presets(presets); - picker.set_date(now, window, cx); - picker.set_disabled(vec![0, 6], window, cx); - picker - }); - let date_picker_large = cx.new(|cx| { - let mut picker = DatePicker::new("date_picker_large", window, cx) - .large() - .date_format("%Y-%m-%d") - .width(px(300.)); - picker.set_disabled( - calendar::Matcher::range(Some(now), now.checked_add_days(Days::new(7))), - window, - cx, - ); - picker.set_date( - now.checked_sub_days(Days::new(1)).unwrap_or_default(), - window, - cx, - ); - picker - }); - let date_picker_small = cx.new(|cx| { - let mut picker = DatePicker::new("date_picker_small", window, cx) - .small() - .width(px(180.)); - picker.set_disabled( - calendar::Matcher::interval(Some(now), now.checked_add_days(Days::new(5))), - window, - cx, - ); - picker.set_date(now, window, cx); - picker - }); - let date_range_picker = cx.new(|cx| { - let mut picker = DatePicker::new("date_range_picker", window, cx) - .width(px(300.)) - .number_of_months(2) - .cleanable() - .presets(range_presets.clone()); - picker.set_date( - (now, now.checked_add_days(Days::new(4)).unwrap()), - window, - cx, - ); - picker - }); - - cx.subscribe(&date_picker, |this, _, ev, _| match ev { - DatePickerEvent::Change(date) => { - this.date_picker_value = date.format("%Y-%m-%d").map(|s| s.to_string()); - } - }) - .detach(); - cx.subscribe(&date_range_picker, |this, _, ev, _| match ev { - DatePickerEvent::Change(date) => { - this.date_picker_value = date.format("%Y-%m-%d").map(|s| s.to_string()); - } - }) - .detach(); - - let default_range_mode_picker = cx.new(|cx| { - DatePicker::range_picker("default_range_mode_picker", window, cx) - .width(px(300.)) - .placeholder("Range mode picker") - .cleanable() - .presets(range_presets.clone()) - }); - - cx.subscribe(&default_range_mode_picker, |this, _, ev, _| match ev { - DatePickerEvent::Change(date) => { - this.date_picker_value = date.format("%Y-%m-%d").map(|s| s.to_string()); - } - }) - .detach(); + let calendar = cx.new(|cx| Calendar::new(window, cx)); + let calendar_wide = cx.new(|cx| Calendar::new(window, cx).number_of_months(3)); Self { - size: Size::default(), - date_picker, - date_picker_large, - date_picker_small, - date_range_picker, - default_range_mode_picker, - date_picker_value: None, + calendar, + calendar_wide, + focus_handle: cx.focus_handle(), } } - - fn change_size(&mut self, size: Size, window: &mut Window, cx: &mut Context) { - self.size = size; - self.date_picker - .update(cx, |picker, cx| picker.set_size(size, window, cx)); - self.date_picker_large - .update(cx, |picker, cx| picker.set_size(size, window, cx)); - self.date_picker_small - .update(cx, |picker, cx| picker.set_size(size, window, cx)); - self.date_range_picker - .update(cx, |picker, cx| picker.set_size(size, window, cx)); - self.default_range_mode_picker - .update(cx, |picker, cx| picker.set_size(size, window, cx)); - } } impl Focusable for CalendarStory { - fn focus_handle(&self, cx: &gpui::App) -> gpui::FocusHandle { - self.date_picker.focus_handle(cx) + fn focus_handle(&self, _: &App) -> gpui::FocusHandle { + self.focus_handle.clone() } } impl Render for CalendarStory { - fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { + fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { v_flex() .gap_3() + .child(section("Normal").max_w_md().child(self.calendar.clone())) .child( - Button::new("change-size") - .label(format!("size: {:?}", self.size)) - .on_click(cx.listener(|this, _, window, cx| match this.size { - Size::Small => this.change_size(Size::Medium, window, cx), - Size::Large => this.change_size(Size::Small, window, cx), - _ => this.change_size(Size::Large, window, cx), - })), + section("With 3 Months") + .max_w_md() + .child(self.calendar_wide.clone()), ) - .child(self.date_picker.clone()) - .child(self.date_picker_small.clone()) - .child(self.date_picker_large.clone()) - .child(self.date_range_picker.clone()) - .child(self.default_range_mode_picker.clone()) - .child(format!("Date picker value: {:?}", self.date_picker_value).into_element()) } } diff --git a/crates/story/src/date_picker_story.rs b/crates/story/src/date_picker_story.rs new file mode 100644 index 00000000..d7005c48 --- /dev/null +++ b/crates/story/src/date_picker_story.rs @@ -0,0 +1,206 @@ +use chrono::{Days, Duration, Utc}; +use gpui::{ + px, App, AppContext, Context, Entity, Focusable, IntoElement, ParentElement as _, Render, + Styled as _, Subscription, Window, +}; +use gpui_component::{ + calendar, + date_picker::{DatePicker, DatePickerEvent, DateRangePreset}, + v_flex, Sizable as _, +}; + +use crate::section; + +pub struct DatePickerStory { + date_picker: Entity, + date_picker_small: Entity, + date_picker_large: Entity, + date_picker_value: Option, + date_range_picker: Entity, + default_range_mode_picker: Entity, + + _subscriptions: Vec, +} + +impl super::Story for DatePickerStory { + fn title() -> &'static str { + "DatePicker" + } + + fn description() -> &'static str { + "A date picker to select a date or date range." + } + + fn new_view(window: &mut Window, cx: &mut App) -> Entity { + Self::view(window, cx) + } +} + +impl DatePickerStory { + pub fn view(window: &mut Window, cx: &mut App) -> Entity { + cx.new(|cx| Self::new(window, cx)) + } + + fn new(window: &mut Window, cx: &mut Context) -> 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 date_picker = cx.new(|cx| { + let mut picker = DatePicker::new("date_picker_medium", window, cx) + .cleanable() + .presets(presets); + picker.set_date(now, window, cx); + picker.set_disabled(vec![0, 6], window, cx); + picker + }); + let date_picker_large = cx.new(|cx| { + let mut picker = DatePicker::new("date_picker_large", window, cx) + .large() + .date_format("%Y-%m-%d") + .width(px(300.)); + picker.set_disabled( + calendar::Matcher::range(Some(now), now.checked_add_days(Days::new(7))), + window, + cx, + ); + picker.set_date( + now.checked_sub_days(Days::new(1)).unwrap_or_default(), + window, + cx, + ); + picker + }); + let date_picker_small = cx.new(|cx| { + let mut picker = DatePicker::new("date_picker_small", window, cx) + .small() + .width(px(180.)); + picker.set_disabled( + calendar::Matcher::interval(Some(now), now.checked_add_days(Days::new(5))), + window, + cx, + ); + picker.set_date(now, window, cx); + picker + }); + let date_range_picker = cx.new(|cx| { + let mut picker = DatePicker::new("date_range_picker", window, cx) + .number_of_months(2) + .cleanable() + .presets(range_presets.clone()); + picker.set_date( + (now, now.checked_add_days(Days::new(4)).unwrap()), + window, + cx, + ); + picker + }); + + let default_range_mode_picker = cx.new(|cx| { + DatePicker::range_picker("default_range_mode_picker", window, cx) + .placeholder("Range mode picker") + .cleanable() + .presets(range_presets.clone()) + }); + + let _subscriptions = vec![ + cx.subscribe(&date_picker, |this, _, ev, _| match ev { + DatePickerEvent::Change(date) => { + this.date_picker_value = date.format("%Y-%m-%d").map(|s| s.to_string()); + } + }), + cx.subscribe(&date_range_picker, |this, _, ev, _| match ev { + DatePickerEvent::Change(date) => { + this.date_picker_value = date.format("%Y-%m-%d").map(|s| s.to_string()); + } + }), + cx.subscribe(&default_range_mode_picker, |this, _, ev, _| match ev { + DatePickerEvent::Change(date) => { + this.date_picker_value = date.format("%Y-%m-%d").map(|s| s.to_string()); + } + }), + ]; + + Self { + date_picker, + date_picker_large, + date_picker_small, + date_range_picker, + default_range_mode_picker, + date_picker_value: None, + _subscriptions, + } + } +} + +impl Focusable for DatePickerStory { + fn focus_handle(&self, cx: &gpui::App) -> gpui::FocusHandle { + self.date_picker.focus_handle(cx) + } +} + +impl Render for DatePickerStory { + fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { + v_flex() + .gap_3() + .child(section("Normal").max_w_md().child(self.date_picker.clone())) + .child( + section("Small with 180px width") + .max_w_md() + .child(self.date_picker_small.clone()), + ) + .child( + section("Large") + .max_w_md() + .child(self.date_picker_large.clone()), + ) + .child( + section("Date Range") + .max_w_md() + .child(self.date_range_picker.clone()), + ) + .child( + section("Default Range Mode") + .max_w_md() + .child(self.default_range_mode_picker.clone()), + ) + .child( + section("Date Picker Value").max_w_md().child( + format!("Date picker value: {:?}", self.date_picker_value).into_element(), + ), + ) + } +} diff --git a/crates/story/src/lib.rs b/crates/story/src/lib.rs index 09856966..5e02e1f0 100644 --- a/crates/story/src/lib.rs +++ b/crates/story/src/lib.rs @@ -6,6 +6,7 @@ mod button_story; mod calendar_story; mod checkbox_story; mod clipboard_story; +mod date_picker_story; mod drawer_story; mod dropdown_story; mod form_story; @@ -54,6 +55,7 @@ pub use button_story::ButtonStory; pub use calendar_story::CalendarStory; pub use checkbox_story::CheckboxStory; pub use clipboard_story::ClipboardStory; +pub use date_picker_story::DatePickerStory; pub use drawer_story::DrawerStory; pub use dropdown_story::DropdownStory; pub use form_story::FormStory; @@ -380,6 +382,7 @@ impl RenderOnce for StorySection { .child( v_flex() .p_4() + .overflow_x_hidden() .border_1() .border_color(cx.theme().border) .rounded_lg() diff --git a/crates/story/src/main.rs b/crates/story/src/main.rs index 20c5548b..f7f54d60 100644 --- a/crates/story/src/main.rs +++ b/crates/story/src/main.rs @@ -41,8 +41,10 @@ impl Gallery { StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), + StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), + StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx), StoryContainer::panel::(window, cx),