diff --git a/frontend/taipy-gui/src/components/Taipy/DateSelector.tsx b/frontend/taipy-gui/src/components/Taipy/DateSelector.tsx index fe1d7b2660..7ca4a907bf 100644 --- a/frontend/taipy-gui/src/components/Taipy/DateSelector.tsx +++ b/frontend/taipy-gui/src/components/Taipy/DateSelector.tsx @@ -28,11 +28,21 @@ import { ErrorBoundary } from "react-error-boundary"; import { createSendUpdateAction } from "../../context/taipyReducers"; import { getSuffixedClassNames, TaipyActiveProps, TaipyChangeProps, DateProps, getProps, getCssSize } from "./utils"; import { dateToString, getDateTime, getTimeZonedDate } from "../../utils"; -import { useClassNames, useDispatch, useDynamicProperty, useFormatConfig, useModule } from "../../utils/hooks"; +import { useClassNames, useDispatch, useDynamicProperty, useFormatConfig, useModule, useDynamicJsonProperty } from "../../utils/hooks"; import Field from "./Field"; import ErrorFallback from "../../utils/ErrorBoundary"; import { getComponentClassName } from "./TaipyStyle"; +interface DisableDateConfig { + disableWeekdays?: boolean; + disableWeekends?: boolean; + disablePastDays?: boolean; + disableFutureDays?: boolean; + dayOfWeek?: number; + interval?: number; + oddInterval?:boolean; + occurrence?: number; +} interface DateSelectorProps extends TaipyActiveProps, TaipyChangeProps { withTime?: boolean; format?: string; @@ -46,7 +56,9 @@ interface DateSelectorProps extends TaipyActiveProps, TaipyChangeProps { editable?: boolean; label?: string; width?: string | number; - analogic? :boolean; + analogic?: boolean; + disableDateConfig?: string; + defaultDisableDateConfig?: string; } const boxSx = { display: "inline-block" }; @@ -74,6 +86,8 @@ const DateSelector = (props: DateSelectorProps) => { const hover = useDynamicProperty(props.hoverText, props.defaultHoverText, undefined); const min = useDynamicProperty(props.min, props.defaultMin, undefined); const max = useDynamicProperty(props.max, props.defaultMax, undefined); + const emptyDateConfig = {} as Partial; + const disableDateConfig = useDynamicJsonProperty(props.disableDateConfig, props.defaultDisableDateConfig || "", emptyDateConfig); const dateSx = useMemo(() => (props.width ? { maxWidth: getCssSize(props.width) } : undefined), [props.width]); @@ -115,6 +129,56 @@ const DateSelector = (props: DateSelectorProps) => { } }, [props.date, tz, withTime, max, min]); + + const getWeekNumberInMonth = (date: Date) => { + const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())); + const firstDayOfMonth = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1)); + d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay() || 7)); + + const weekNo: number = Math.ceil(((d.getTime() - firstDayOfMonth.getTime()) / 86400000 + 1) / 7); + return weekNo; + } + const isDisabledDate = (date: Date) => { + if (disableDateConfig) { + if (disableDateConfig.disableWeekdays && (date.getDay() == 0 || date.getDay() == 6)) { + return true; + } + if (disableDateConfig.disableWeekdays && (date.getDay() != 0 || date.getDay() != 6)) { + return true; + } + if (disableDateConfig.disablePastDays && (date < new Date())) { + return true + } + if (disableDateConfig.disableFutureDays && (date > new Date())) { + return true; + } + + if (disableDateConfig.dayOfWeek) { + const isCorrectDay = date.getDay() === disableDateConfig.dayOfWeek; + const weekNumberInMonth = getWeekNumberInMonth(date); + const intervalCheck=disableDateConfig.oddInterval?1:0; + if (isCorrectDay && !disableDateConfig.interval && !disableDateConfig.occurrence) { + return true; + } + if (isCorrectDay && disableDateConfig.interval) { + if (weekNumberInMonth % disableDateConfig.interval === intervalCheck) { + return true; + } + } + if (isCorrectDay && disableDateConfig.occurrence) { + const dayOfMonth = new Date(date.getFullYear(), date.getMonth(), 1); + const dayDifference = (disableDateConfig.dayOfWeek - dayOfMonth.getDay() + 7) % 7; + const occurrenceDate = new Date(date.getFullYear(), date.getMonth(), 1 + dayDifference + (7 * (disableDateConfig.occurrence-1))); + if(occurrenceDate.getDate()==date.getDate()){ + return true; + } + } + } + } + return false + + }; + return ( @@ -122,18 +186,19 @@ const DateSelector = (props: DateSelectorProps) => { {editable ? ( withTime ? ( )} - {...(endProps as DateTimePickerProps)} - value={value} - onChange={handleChange} - className={getSuffixedClassNames(className, "-picker")} - disabled={!active} - slotProps={textFieldProps} - label={props.label} - format={props.format} - sx={dateSx} - viewRenderers={ analogic ? analogicRenderers : undefined } - /> + {...(startProps as DateTimePickerProps)} + {...(endProps as DateTimePickerProps)} + value={value} + onChange={handleChange} + className={getSuffixedClassNames(className, "-picker")} + disabled={!active} + slotProps={textFieldProps} + label={props.label} + format={props.format} + sx={dateSx} + viewRenderers={analogic ? analogicRenderers : undefined} + shouldDisableDate={isDisabledDate} + /> ) : ( )} @@ -146,6 +211,7 @@ const DateSelector = (props: DateSelectorProps) => { label={props.label} format={props.format} sx={dateSx} + shouldDisableDate={isDisabledDate} /> ) ) : ( diff --git a/taipy/gui/_renderers/factory.py b/taipy/gui/_renderers/factory.py index 400b3586fc..6dc9060788 100644 --- a/taipy/gui/_renderers/factory.py +++ b/taipy/gui/_renderers/factory.py @@ -172,6 +172,7 @@ class _Factory: ("on_change", PropertyType.function), ("format",), ("width", PropertyType.string_or_number), + ("disable_date_config",PropertyType.dict) ] ) ._set_propagate(),