11//! Defines the [`DatePicker`] and [`DateRangePicker`] components and its subcomponents, which allowing users to enter or select a date value
22
33use crate :: {
4- calendar:: { weekday_abbreviation, CalendarProps , DateRange , RangeCalendarProps } ,
4+ calendar:: {
5+ weekday_abbreviation, AvailableRanges , CalendarProps , DateRange , RangeCalendarProps ,
6+ } ,
57 dioxus_core:: Properties ,
68 focus:: { use_focus_controlled_item, use_focus_provider, FocusState } ,
79 popover:: * ,
@@ -24,6 +26,7 @@ struct BaseDatePickerContext {
2426 disabled : ReadSignal < bool > ,
2527 focus : FocusState ,
2628 enabled_date_range : DateRange ,
29+ available_ranges : Memo < AvailableRanges > ,
2730}
2831
2932/// The context provided by the [`DatePicker`] component to its children.
@@ -69,6 +72,10 @@ pub struct DatePickerProps {
6972 #[ props( default = date!( 2050 -12 -31 ) ) ]
7073 pub max_date : Date ,
7174
75+ /// Unavailable dates
76+ #[ props( default ) ]
77+ pub disabled_ranges : ReadSignal < Vec < DateRange > > ,
78+
7279 /// Whether focus should loop around when reaching the end.
7380 #[ props( default = ReadSignal :: new( Signal :: new( false ) ) ) ]
7481 pub roving_loop : ReadSignal < bool > ,
@@ -128,6 +135,7 @@ pub struct DatePickerProps {
128135pub fn DatePicker ( props : DatePickerProps ) -> Element {
129136 let open = use_signal ( || false ) ;
130137 let focus = use_focus_provider ( props. roving_loop ) ;
138+ let available_ranges = use_memo ( move || AvailableRanges :: new ( & props. disabled_ranges . read ( ) ) ) ;
131139
132140 // Create context provider for child components
133141 use_context_provider ( || BaseDatePickerContext {
@@ -136,6 +144,7 @@ pub fn DatePicker(props: DatePickerProps) -> Element {
136144 disabled : props. disabled ,
137145 focus,
138146 enabled_date_range : DateRange :: new ( props. min_date , props. max_date ) ,
147+ available_ranges,
139148 } ) ;
140149
141150 use_context_provider ( || DatePickerContext {
@@ -199,6 +208,10 @@ pub struct DateRangePickerProps {
199208 #[ props( default = date!( 2050 -12 -31 ) ) ]
200209 pub max_date : Date ,
201210
211+ /// Unavailable dates
212+ #[ props( default ) ]
213+ pub disabled_ranges : ReadSignal < Vec < DateRange > > ,
214+
202215 /// Whether focus should loop around when reaching the end.
203216 #[ props( default = ReadSignal :: new( Signal :: new( false ) ) ) ]
204217 pub roving_loop : ReadSignal < bool > ,
@@ -258,13 +271,16 @@ pub fn DateRangePicker(props: DateRangePickerProps) -> Element {
258271 let open = use_signal ( || false ) ;
259272 let focus = use_focus_provider ( props. roving_loop ) ;
260273
274+ let available_ranges = use_memo ( move || AvailableRanges :: new ( & props. disabled_ranges . read ( ) ) ) ;
275+
261276 // Create context provider for child components
262277 use_context_provider ( || BaseDatePickerContext {
263278 open,
264279 read_only : props. read_only ,
265280 disabled : props. disabled ,
266281 focus,
267282 enabled_date_range : DateRange :: new ( props. min_date , props. max_date ) ,
283+ available_ranges,
268284 } ) ;
269285
270286 let date_range = use_signal ( || ( props. selected_range ) ( ) ) ;
@@ -410,6 +426,10 @@ pub struct DatePickerCalendarProps<T: Properties + PartialEq> {
410426 #[ props( default = date!( 2050 -12 -31 ) ) ]
411427 pub max_date : Date ,
412428
429+ /// Unavailable dates
430+ #[ props( default ) ]
431+ pub disabled_ranges : ReadSignal < Vec < DateRange > > ,
432+
413433 /// Additional attributes to extend the calendar element
414434 #[ props( extends = GlobalAttributes ) ]
415435 pub attributes : Vec < Attribute > ,
@@ -484,6 +504,7 @@ pub fn DatePickerCalendar(props: DatePickerCalendarProps<CalendarProps>) -> Elem
484504 ctx. set_date( date) ;
485505 base_ctx. open. set( false ) ;
486506 } ,
507+ disabled_ranges: base_ctx. available_ranges. read( ) . to_disabled_ranges( ) ,
487508 on_format_weekday: props. on_format_weekday,
488509 on_format_month: props. on_format_month,
489510 view_date: view_date( ) ,
@@ -561,6 +582,7 @@ pub fn DateRangePickerCalendar(props: DatePickerCalendarProps<RangeCalendarProps
561582 ctx. set_range( range) ;
562583 base_ctx. open. set( false ) ;
563584 } ,
585+ disabled_ranges: base_ctx. available_ranges. read( ) . to_disabled_ranges( ) ,
564586 on_format_weekday: props. on_format_weekday,
565587 on_format_month: props. on_format_month,
566588 view_date: view_date( ) ,
@@ -857,8 +879,13 @@ fn DateElement(props: DateElementProps) -> Element {
857879 . ok ( )
858880 . filter ( |date| ctx. enabled_date_range . contains ( * date) )
859881 {
882+ let new_date = if ctx. available_ranges . read ( ) . valid_interval ( date) {
883+ Some ( date)
884+ } else {
885+ None
886+ } ;
860887 tracing:: info!( "Parsed date: {date:?}" ) ;
861- props. on_date_change . call ( Some ( date ) ) ;
888+ props. on_date_change . call ( new_date ) ;
862889 }
863890 }
864891 } ) ;
@@ -1069,7 +1096,7 @@ pub fn DateRangePickerInput(props: DatePickerInputProps) -> Element {
10691096 use_effect ( move || {
10701097 if let ( Some ( start) , Some ( end) ) = ( start_date ( ) , end_date ( ) ) {
10711098 let range = Some ( DateRange :: new ( start, end) ) ;
1072- tracing :: info! ( "set range {range:?}" ) ;
1099+
10731100 ctx. set_range ( range) ;
10741101 } ;
10751102 } ) ;
0 commit comments