@@ -487,7 +487,7 @@ impl RangeCalendarContext {
487487 }
488488}
489489
490- /// The props for the [`Calendar `] component.
490+ /// The props for the [`RangeCalendar `] component.
491491#[ derive( Props , Clone , PartialEq ) ]
492492pub struct RangeCalendarProps {
493493 /// The selected range
@@ -578,7 +578,11 @@ pub struct RangeCalendarProps {
578578/// }
579579/// }
580580/// }
581- /// CalendarGrid {}
581+ /// CalendarGrid {
582+ /// render_day: Callback::new(|date: Date| {
583+ /// rsx! { RangeCalendarDay { date } }
584+ /// })
585+ /// }
582586/// }
583587/// }
584588/// }
@@ -624,9 +628,19 @@ pub fn RangeCalendar(props: RangeCalendarProps) -> Element {
624628 aria_label: "Calendar" ,
625629 "data-disabled" : ( props. disabled) ( ) ,
626630 onkeydown: move |e| {
627- let Some ( focused_date) = ( base_ctx. focused_date) ( ) else {
631+ let Some ( mut focused_date) = ( base_ctx. focused_date) ( ) else {
628632 return ;
629633 } ;
634+
635+ // force hover day as focus
636+ if let ( Some ( range) , Some ( date) ) = ( ( ctx. highlighted_range) ( ) , ( ctx. anchor_date) ( ) ) {
637+ if date != range. start {
638+ focused_date = range. start
639+ } else {
640+ focused_date = range. end
641+ }
642+ } ;
643+
630644 let mut set_focused_date = |new_date: Option <Date >| {
631645 // Make sure the view date month is the same as the focused date
632646 let mut view_date = ( base_ctx. view_date) ( ) ;
@@ -1607,6 +1621,63 @@ fn CalendarDay(props: CalendarDayProps) -> Element {
16071621 }
16081622}
16091623
1624+ /// # RangeCalendarDay
1625+ ///
1626+ /// The [`RangeCalendarDay`] component provides an accessible calendar interface for date
1627+ ///
1628+ /// This must be used inside a [`CalendarGrid`] component.
1629+ ///
1630+ /// ## Example
1631+ /// ```rust
1632+ /// use dioxus::prelude::*;
1633+ /// use dioxus_primitives::calendar::*;
1634+ /// use time::{Date, Month, UtcDateTime};
1635+ /// #[component]
1636+ /// fn Demo() -> Element {
1637+ /// let mut selected_range = use_signal(|| None::<DateRange>);
1638+ /// let mut view_date = use_signal(|| UtcDateTime::now().date());
1639+ /// rsx! {
1640+ /// RangeCalendar {
1641+ /// selected_range: selected_range(),
1642+ /// on_range_change: move |range| {
1643+ /// tracing::info!("Selected range: {:?}", range);
1644+ /// selected_range.set(range);
1645+ /// },
1646+ /// view_date: view_date(),
1647+ /// on_view_change: move |new_view: Date| {
1648+ /// tracing::info!("View changed to: {}-{}", new_view.year(), new_view.month());
1649+ /// view_date.set(new_view);
1650+ /// },
1651+ /// CalendarHeader {
1652+ /// CalendarNavigation {
1653+ /// CalendarPreviousMonthButton {
1654+ /// "<"
1655+ /// }
1656+ /// CalendarMonthTitle {}
1657+ /// CalendarNextMonthButton {
1658+ /// ">"
1659+ /// }
1660+ /// }
1661+ /// }
1662+ /// CalendarGrid {
1663+ /// render_day: Callback::new(|date: Date| {
1664+ /// rsx! { RangeCalendarDay { date } }
1665+ /// })
1666+ /// }
1667+ /// }
1668+ /// }
1669+ /// }
1670+ /// ```
1671+ ///
1672+ /// # Styling
1673+ ///
1674+ /// The [`RangeCalendarDay`] component defines the following data attributes you can use to control styling:
1675+ /// - `data-disabled`: Indicates if the calendar is disabled. Possible values are `true` or `false`.
1676+ /// - `data-today`: Indicates if the cell is today. Possible values are `true` or `false`.
1677+ /// - `data-selected`: Indicates if the cell is selected. Possible values are `true` or `false`.
1678+ /// - `date-selection-start`: Indicates if cell is the first date in a range selection. Possible values are `true` or `false`.
1679+ /// - `date-selection-between`: Indicates if a date interval contains a cell. Possible values are `true` or `false`.
1680+ /// - `date-selection-end`: Indicates if cell is the last date in a range selection. Possible values are `true` or `false`.
16101681#[ component]
16111682pub fn RangeCalendarDay ( props : CalendarDayProps ) -> Element {
16121683 let CalendarDayProps { date, attributes } = props;
@@ -1631,7 +1702,7 @@ pub fn RangeCalendarDay(props: CalendarDayProps) -> Element {
16311702 } ;
16321703 let in_current_month = month == RelativeMonth :: Current ;
16331704 let is_selected = move || ( ctx. highlighted_range ) ( ) . is_some_and ( |r| r. contains ( date) ) ;
1634- let is_center =
1705+ let is_between =
16351706 move || ( ctx. highlighted_range ) ( ) . is_some_and ( |r| r. contained_in_interval ( date) ) ;
16361707 let is_start =
16371708 move || ( ctx. highlighted_range ) ( ) . is_some_and ( |d| d. start == date && date != d. end ) ;
@@ -1685,7 +1756,7 @@ pub fn RangeCalendarDay(props: CalendarDayProps) -> Element {
16851756 "data-today" : is_today,
16861757 "data-selected" : is_selected( ) ,
16871758 "date-selection-start" : if is_start( ) { true } ,
1688- "date-selection-center " : if is_center ( ) { true } ,
1759+ "date-selection-between " : if is_between ( ) { true } ,
16891760 "date-selection-end" : if is_end( ) { true } ,
16901761 "data-month" : "{month}" ,
16911762 onclick: move |e| {
@@ -1699,7 +1770,11 @@ pub fn RangeCalendarDay(props: CalendarDayProps) -> Element {
16991770 base_ctx. focused_date. set( Some ( date) ) ;
17001771 }
17011772 } ,
1702- onmouseover: move |_| ctx. set_hovered_date( date) ,
1773+ onmouseover: move |_| {
1774+ if in_current_month {
1775+ ctx. set_hovered_date( date)
1776+ }
1777+ } ,
17031778 onmounted: move |e| day_ref. set( Some ( e. data( ) ) ) ,
17041779 ..attributes,
17051780 { day. to_string( ) }
0 commit comments