浏览代码

Adding min & max date attribute for DateSelector.tsx

namnguyen 10 月之前
父节点
当前提交
c27a85457d

+ 27 - 0
doc/gui/examples/controls/date-min-max-range.py

@@ -0,0 +1,27 @@
+# Copyright 2021-2024 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+# -----------------------------------------------------------------------------------------
+# To execute this script, make sure that the taipy-gui package is installed in your
+# Python environment and run:
+#     python <script>
+# -----------------------------------------------------------------------------------------
+from taipy.gui import Gui
+import datetime
+
+date = datetime.date(2024, 6, 15)
+min_date = datetime.date(2024, 5, 15)
+max_date = datetime.date(2024, 7, 15)
+
+page = """
+<|{date}|date|min_date={min_date}|max_date={max_date}|>
+"""
+
+Gui(page).run()

+ 1 - 27
frontend/taipy-gui/src/components/Taipy/DateRange.tsx

@@ -21,7 +21,7 @@ import { isValid } from "date-fns";
 import { ErrorBoundary } from "react-error-boundary";
 
 import { createSendUpdateAction } from "../../context/taipyReducers";
-import { getSuffixedClassNames, TaipyActiveProps, TaipyChangeProps } from "./utils";
+import { getSuffixedClassNames, TaipyActiveProps, TaipyChangeProps, DateProps, getProps } from "./utils";
 import { dateToString, getDateTime, getTimeZonedDate } from "../../utils";
 import { useClassNames, useDispatch, useDynamicProperty, useFormatConfig, useModule } from "../../utils/hooks";
 import Field from "./Field";
@@ -60,32 +60,6 @@ const getRangeDateTime = (
     return [null, null];
 };
 
-interface DateProps {
-    maxDate?: unknown;
-    maxDateTime?: unknown;
-    maxTime?: unknown;
-    minDate?: unknown;
-    minDateTime?: unknown;
-    minTime?: unknown;
-}
-
-const getProps = (p: DateProps, start: boolean, val: Date | null, withTime: boolean): DateProps => {
-    if (!val) {
-        return {};
-    }
-    const propName: keyof DateProps = withTime
-        ? start
-            ? "minDateTime"
-            : "maxDateTime"
-        : start
-        ? "minDate"
-        : "maxDate";
-    if (p[propName] == val) {
-        return p;
-    }
-    return { ...p, [propName]: val };
-};
-
 const DateRange = (props: DateRangeProps) => {
     const { updateVarName, withTime = false, id, propagate = true } = props;
     const dispatch = useDispatch();

+ 21 - 7
frontend/taipy-gui/src/components/Taipy/DateSelector.tsx

@@ -14,14 +14,14 @@
 import React, { useState, useEffect, useCallback } from "react";
 import Box from "@mui/material/Box";
 import Tooltip from "@mui/material/Tooltip";
-import { DatePicker } from "@mui/x-date-pickers/DatePicker";
+import { DatePicker, DatePickerProps } from "@mui/x-date-pickers/DatePicker";
 import { BaseDateTimePickerSlotProps } from "@mui/x-date-pickers/DateTimePicker/shared";
-import { DateTimePicker } from "@mui/x-date-pickers/DateTimePicker";
+import { DateTimePicker, DateTimePickerProps } from "@mui/x-date-pickers/DateTimePicker";
 import { isValid } from "date-fns";
 import { ErrorBoundary } from "react-error-boundary";
 
 import { createSendUpdateAction } from "../../context/taipyReducers";
-import { getSuffixedClassNames, TaipyActiveProps, TaipyChangeProps } from "./utils";
+import { getSuffixedClassNames, TaipyActiveProps, TaipyChangeProps, DateProps, getProps } from "./utils";
 import { dateToString, getDateTime, getTimeZonedDate } from "../../utils";
 import { useClassNames, useDispatch, useDynamicProperty, useFormatConfig, useModule } from "../../utils/hooks";
 import Field from "./Field";
@@ -31,6 +31,8 @@ interface DateSelectorProps extends TaipyActiveProps, TaipyChangeProps {
     withTime?: boolean;
     format?: string;
     date: string;
+    minDate?: string;
+    maxDate?: string;
     defaultDate?: string;
     defaultEditable?: boolean;
     editable?: boolean;
@@ -46,6 +48,8 @@ const DateSelector = (props: DateSelectorProps) => {
     const formatConfig = useFormatConfig();
     const tz = formatConfig.timeZone;
     const [value, setValue] = useState(() => getDateTime(props.defaultDate, tz, withTime));
+    const [startProps, setStartProps] = useState<DateProps>({});
+    const [endProps, setEndProps] = useState<DateProps>({});
     const module = useModule();
 
     const className = useClassNames(props.libClassName, props.dynamicClassName, props.className);
@@ -64,12 +68,12 @@ const DateSelector = (props: DateSelectorProps) => {
                         dateToString(newDate, withTime),
                         module,
                         props.onChange,
-                        propagate
-                    )
+                        propagate,
+                    ),
                 );
             }
         },
-        [updateVarName, dispatch, withTime, propagate, tz, props.onChange, module]
+        [updateVarName, dispatch, withTime, propagate, tz, props.onChange, module],
     );
 
     // Run every time props.value get updated
@@ -77,7 +81,13 @@ const DateSelector = (props: DateSelectorProps) => {
         if (props.date !== undefined) {
             setValue(getDateTime(props.date, tz, withTime));
         }
-    }, [props.date, tz, withTime]);
+        if (props.minDate !== null) {
+            setStartProps((p) => getProps(p, true, getDateTime(props.minDate, tz, withTime), withTime));
+        }
+        if (props.maxDate !== null) {
+            setEndProps((p) => getProps(p, false, getDateTime(props.maxDate, tz, withTime), withTime));
+        }
+    }, [props.date, props.minDate, props.maxDate, tz, withTime]);
 
     return (
         <ErrorBoundary FallbackComponent={ErrorFallback}>
@@ -86,6 +96,8 @@ const DateSelector = (props: DateSelectorProps) => {
                     {editable ? (
                         withTime ? (
                             <DateTimePicker
+                                {...(startProps as DateTimePickerProps<Date>)}
+                                {...(endProps as DateTimePickerProps<Date>)}
                                 value={value}
                                 onChange={handleChange}
                                 className={getSuffixedClassNames(className, "-picker")}
@@ -96,6 +108,8 @@ const DateSelector = (props: DateSelectorProps) => {
                             />
                         ) : (
                             <DatePicker
+                                {...(startProps as DatePickerProps<Date>)}
+                                {...(endProps as DatePickerProps<Date>)}
                                 value={value}
                                 onChange={handleChange}
                                 className={getSuffixedClassNames(className, "-picker")}

+ 26 - 0
frontend/taipy-gui/src/components/Taipy/utils.ts

@@ -67,6 +67,15 @@ export interface TaipyLabelProps {
     label?: string;
 }
 
+export interface DateProps {
+    maxDate?: unknown;
+    maxDateTime?: unknown;
+    maxTime?: unknown;
+    minDate?: unknown;
+    minDateTime?: unknown;
+    minTime?: unknown;
+}
+
 export const getArrayValue = <T>(arr: T[], idx: number, defVal?: T): T | undefined => {
     const val = Array.isArray(arr) && idx < arr.length ? arr[idx] : undefined;
     return val ?? defVal;
@@ -121,3 +130,20 @@ export const getSuffixedClassNames = (names: string | undefined, suffix: string)
 export const emptyStyle = {} as CSSProperties;
 
 export const disableColor = <T>(color: T, disabled: boolean) => (disabled ? ("disabled" as T) : color);
+
+export const getProps = (p: DateProps, start: boolean, val: Date | null, withTime: boolean): DateProps => {
+    if (!val) {
+        return {};
+    }
+    const propName: keyof DateProps = withTime
+        ? start
+            ? "minDateTime"
+            : "maxDateTime"
+        : start
+            ? "minDate"
+            : "maxDate";
+    if (p[propName] == val) {
+        return p;
+    }
+    return {...p, [propName]: val};
+};

+ 11 - 0
taipy/gui/_renderers/builder.py

@@ -312,6 +312,15 @@ class _Builder:
             return self
         return self.set_attribute(_to_camel_case(name), str(strattr))
 
+    def __set_date_attribute(self, name: str, default_value: t.Optional[str] = None, optional: t.Optional[bool] = True):
+        dateattr = self.__attributes.get(name, default_value)
+        if dateattr is None:
+            if not optional:
+                _warn(f"Property {name} is required for control {self.__control_type}.")
+            return self
+        value = _date_to_string(dateattr)
+        return self.set_attribute(_to_camel_case(name), value)
+
     def __set_dynamic_string_attribute(
         self,
         name: str,
@@ -1012,6 +1021,8 @@ class _Builder:
                     self.__set_dynamic_bool_attribute(attr[0], _get_tuple_val(attr, 2, False), True, update_main=False)
                 else:
                     self.__set_dynamic_string_list(attr[0], _get_tuple_val(attr, 2, None))
+            elif var_type == PropertyType.date:
+                self.__set_date_attribute(attr[0], _get_tuple_val(attr, 2, None), _get_tuple_val(attr, 3, True))
             elif var_type == PropertyType.data:
                 self.__set_dynamic_property_without_default(attr[0], var_type)
             elif var_type == PropertyType.lov or var_type == PropertyType.single_lov:

+ 2 - 0
taipy/gui/_renderers/factory.py

@@ -138,6 +138,8 @@ class _Factory:
             [
                 ("with_time", PropertyType.boolean),
                 ("active", PropertyType.dynamic_boolean, True),
+                ("min_date", PropertyType.date),
+                ("max_date", PropertyType.date),
                 ("editable", PropertyType.dynamic_boolean, True),
                 ("hover_text", PropertyType.dynamic_string),
                 ("label",),