Skip to content

Commit

Permalink
Adding min & max date attribute for DateSelector.tsx
Browse files Browse the repository at this point in the history
  • Loading branch information
namnguyen20999 committed Jun 30, 2024
1 parent c5618f1 commit 19676e3
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 34 deletions.
27 changes: 27 additions & 0 deletions doc/gui/examples/controls/date-min-max-range.py
Original file line number Diff line number Diff line change
@@ -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()
28 changes: 1 addition & 27 deletions frontend/taipy-gui/src/components/Taipy/DateRange.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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();
Expand Down
28 changes: 21 additions & 7 deletions frontend/taipy-gui/src/components/Taipy/DateSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -64,20 +68,26 @@ 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
useEffect(() => {
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}>
Expand All @@ -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")}
Expand All @@ -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")}
Expand Down
26 changes: 26 additions & 0 deletions frontend/taipy-gui/src/components/Taipy/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,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;
Expand Down Expand Up @@ -113,3 +122,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 changes: 11 additions & 0 deletions taipy/gui/_renderers/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,15 @@ def __set_string_attribute(
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,
Expand Down Expand Up @@ -1012,6 +1021,8 @@ def set_attributes(self, attributes: t.List[tuple]): # noqa: C901
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:
Expand Down
2 changes: 2 additions & 0 deletions taipy/gui/_renderers/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",),
Expand Down

0 comments on commit 19676e3

Please sign in to comment.