From c295cbc801b700245c5f61b875274ee490a9dce9 Mon Sep 17 00:00:00 2001 From: Joe Numainville Date: Fri, 5 Apr 2024 16:14:50 -0500 Subject: [PATCH 1/9] wip --- plugins/ui/DESIGN.md | 2 +- .../deephaven/ui/components/date_picker.py | 192 ++++++++++++++++++ plugins/ui/src/deephaven/ui/types/types.py | 28 +++ 3 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 plugins/ui/src/deephaven/ui/components/date_picker.py diff --git a/plugins/ui/DESIGN.md b/plugins/ui/DESIGN.md index 071872caa..f3316cf02 100644 --- a/plugins/ui/DESIGN.md +++ b/plugins/ui/DESIGN.md @@ -1385,7 +1385,7 @@ ui.date_picker( granularity: Granularity | None = None, on_change: Callable[[Date], None] | None = None, **props: Any -) -> ListViewElement +) -> DatePickerElement ``` ###### Parameters diff --git a/plugins/ui/src/deephaven/ui/components/date_picker.py b/plugins/ui/src/deephaven/ui/components/date_picker.py new file mode 100644 index 000000000..ed57713f7 --- /dev/null +++ b/plugins/ui/src/deephaven/ui/components/date_picker.py @@ -0,0 +1,192 @@ +from __future__ import annotations + +from typing import Any, Sequence, Callable + +from deephaven.dtypes import Instant, ZonedDateTime, LocalDate +from deephaven.time import to_j_instant, to_j_zdt, to_j_local_date + +from ..elements import Element +from .._internal.utils import create_props, wrap_callable +from ..types import Date, Granularity + +DatePickerElement = Element + + +def convert_to_java_date( + date: Date, +) -> Instant | ZonedDateTime | LocalDate: # type: ignore + """ + Convert a Date to a Java date type. + In order of preference, tries to convert to Instant, ZonedDateTime, and LocalDate. + If none of these work, raises a TypeError. + + Args: + date: The date to convert to a Java date type. + + Returns: + The Java date type. + """ + try: + return to_j_instant(date) + except TypeError: + # ignore, try next + pass + + try: + return to_j_zdt(date) + except TypeError: + # ignore, try next + pass + + try: + return to_j_local_date(date) + except TypeError: + raise TypeError( + f"Could not convert {date} to one of Instant, ZonedDateTime, or LocalDate." + ) + + +def date_converter( + props: dict[str, Any], +) -> Callable[[Date], Instant | ZonedDateTime | LocalDate]: # type: ignore + """ + Go through "value", "default_value", and "placeholder_value" in props to + determine the type of date converter to use. + If none of these are present, defaults to Instant. + This is used to convert callback arguments to Java date types. + + Args: + props: The props to check for date types. + + Returns: + The date converter. + """ + converters = { + Instant: to_j_instant, + ZonedDateTime: to_j_zdt, + LocalDate: to_j_local_date, + } + + if "value" in props: + return converters[type(props["value"])] + elif "default_value" in props: + return converters[type(props["default_value"])] + elif "placeholder_value" in props: + return converters[type(props["placeholder_value"])] + + return to_j_instant + + +def wrap_date_callable( + date_callable: Callable[[Date], None], + converter: Callable[[Date], Any], +) -> Callable[[Date], None]: + """ + Wrap a callable to convert the Date argument to a Java date type. + This maintains the original callable signature so that the Date argument can be dropped. + + Args: + date_callable: The callable to wrap. + converter: The date converter to use. + + Returns: + The wrapped callable. + """ + return lambda date: wrap_callable(date_callable)(converter(date)) + + +def convert_date_props( + props: dict[str, Any], + simple_date_props: set[str], + list_date_props: set[str], + callable_date_props: set[str], + converter: Callable[[Date], Any], +) -> None: + """ + Convert date props to Java date types in place. + + Args: + props: The props passed to the component. + simple_date_props: A set of simple date keys to convert. The prop value should be a single Date. + list_date_props: A set of list date keys to convert. The prop value should be a list of Dates. + callable_date_props: A set of callable date keys to convert. + The prop value should be a callable that takes a Date. + converter: The date converter to use. + + Returns: + The converted props. + """ + for key in simple_date_props: + if key in props: + props[key] = convert_to_java_date(props[key]) + + for key in list_date_props: + if key in props: + if not isinstance(props[key], list): + raise TypeError(f"{key} must be a list of Dates") + props[key] = [convert_to_java_date(date) for date in props[key]] + + for key in callable_date_props: + if key in props: + if not callable(props[key]): + raise TypeError(f"{key} must be a callable") + props[key] = wrap_date_callable(props[key], converter) + + +def convert_date_picker_props( + props: dict[str, Any], +) -> dict[str, Any]: + """ + Convert date picker props to Java date types. + + Args: + props: The props passed to the date picker. + + Returns: + The converted props. + """ + simple_date_props = { + "placeholder_value", + "value", + "default_value", + "min_value", + "max_value", + } + list_date_props = {"unavailable_values"} + callable_date_props = {"on_change"} + + converter = date_converter(props) + + convert_date_props( + props, simple_date_props, list_date_props, callable_date_props, converter + ) + + return props + + +def date_picker( + placeholder_value: Date | None = None, + value: Date | None = None, + default_value: Date | None = None, + min_value: Date | None = None, + max_value: Date | None = None, + unavailable_values: Sequence[Date] | None = None, + granularity: Granularity | None = None, + on_change: Callable[[Date], None] | None = None, + **props: Any, +) -> DatePickerElement | None: + """ + + + Args: + + + Returns: + """ + _, props = create_props(locals()) + + convert_date_picker_props(props) + + print(props) + + return None # BaseElement("deephaven.ui.components.DatePicker", **props) diff --git a/plugins/ui/src/deephaven/ui/types/types.py b/plugins/ui/src/deephaven/ui/types/types.py index 90bfd8480..69db7a10e 100644 --- a/plugins/ui/src/deephaven/ui/types/types.py +++ b/plugins/ui/src/deephaven/ui/types/types.py @@ -1,5 +1,9 @@ +import datetime +import pandas +import numpy from typing import Any, Dict, Literal, Union, List, Tuple, Callable, TypedDict, Sequence from deephaven import SortDirection +from deephaven.dtypes import LocalDate, Instant, ZonedDateTime class CellData(TypedDict): @@ -106,6 +110,30 @@ class RowDataValue(CellData): Stringable = Union[str, int, float, bool] Key = Stringable ActionKey = Key +LocalDateConvertible = Union[ + None, + LocalDate, # type: ignore + str, + datetime.date, + datetime.datetime, + numpy.datetime64, + pandas.Timestamp, +] +InstantConvertible = Union[ + None, Instant, int, str, datetime.datetime, numpy.datetime64, pandas.Timestamp # type: ignore +] +ZonedDateTimeConvertible = Union[ + None, ZonedDateTime, str, datetime.datetime, numpy.datetime64, pandas.Timestamp # type: ignore +] +Date = Union[ + Instant, # type: ignore + LocalDate, # type: ignore + ZonedDateTime, # type: ignore + LocalDateConvertible, + InstantConvertible, + ZonedDateTimeConvertible, +] +Granularity = Literal["DAY", "HOUR", "MINUTE", "SECOND"] Dependencies = Union[Tuple[Any], List[Any]] Selection = Sequence[Key] From 5d73861d2600f49e1081bf6d8f25c5ea391a0771 Mon Sep 17 00:00:00 2001 From: Joe Numainville Date: Tue, 9 Apr 2024 12:38:02 -0500 Subject: [PATCH 2/9] wip --- .../ui/src/deephaven/ui/_internal/utils.py | 149 +++++++++++++++ .../src/deephaven/ui/components/__init__.py | 2 + .../deephaven/ui/components/date_picker.py | 175 ++++-------------- 3 files changed, 183 insertions(+), 143 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/_internal/utils.py b/plugins/ui/src/deephaven/ui/_internal/utils.py index 1cd6b66e3..e29ce21d3 100644 --- a/plugins/ui/src/deephaven/ui/_internal/utils.py +++ b/plugins/ui/src/deephaven/ui/_internal/utils.py @@ -4,11 +4,27 @@ from inspect import signature import sys from functools import partial +from deephaven.dtypes import Instant, ZonedDateTime, LocalDate +from deephaven.time import to_j_instant, to_j_zdt, to_j_local_date + +from ..types import Date + +DateConverter = ( + Callable[[Date], Instant] # type: ignore + | Callable[[Date], ZonedDateTime] # type: ignore + | Callable[[Date], LocalDate] # type: ignore +) _UNSAFE_PREFIX = "UNSAFE_" _ARIA_PREFIX = "aria_" _ARIA_PREFIX_REPLACEMENT = "aria-" +_CONVERTERS = { + "java.time.Instant": to_j_instant, + "java.time.ZonedDateTime": to_j_zdt, + "java.time.LocalDate": to_j_local_date, +} + def get_component_name(component: Any) -> str: """ @@ -197,3 +213,136 @@ def create_props(args: dict[str, Any]) -> tuple[tuple[Any], dict[str, Any]]: children, props = args.pop("children"), args.pop("props") props.update(args) return children, props + + +def _convert_to_java_date( + date: Date, +) -> Instant | ZonedDateTime | LocalDate: # type: ignore + """ + Convert a Date to a Java date type. + In order of preference, tries to convert to Instant, ZonedDateTime, and LocalDate. + If none of these work, raises a TypeError. + + Args: + date: The date to convert to a Java date type. + + Returns: + The Java date type. + """ + try: + return to_j_instant(date) + except Exception: + # ignore, try next + pass + + try: + return to_j_zdt(date) + except Exception: + # ignore, try next + pass + + try: + return to_j_local_date(date) + except Exception: + raise TypeError( + f"Could not convert {date} to one of Instant, ZonedDateTime, or LocalDate." + ) + + +def _jclass_converter( + value: Instant | ZonedDateTime | LocalDate, # type: ignore +) -> Callable[[Date], Any]: + """ + Get the converter for the Java date type. + + Args: + value: The Java date type to get the converter for. + + Returns: + The converter for the Java date type. + """ + return _CONVERTERS[str(value.jclass)[6:]] + + +def _wrap_date_callable( + date_callable: Callable[[Date], None], + converter: Callable[[Date], Any], +) -> Callable[[Date], None]: + """ + Wrap a callable to convert the Date argument to a Java date type. + This maintains the original callable signature so that the Date argument can be dropped. + + Args: + date_callable: The callable to wrap. + converter: The date converter to use. + + Returns: + The wrapped callable. + """ + return lambda date: wrap_callable(date_callable)(converter(date)) + + +def prioritized_callable_date_converter( + props: dict[str, Any], + priority: Sequence[str], + default_converter: Callable[[Date], Any], +) -> DateConverter: + """ + Get a callable date converter based on the priority of the props. + If none of the priority props are present, uses the default converter. + + Args: + props: The props passed to the component. + priority: The priority of the props to check. + default_converter: The default converter to use if none of the priority props are present. + + Returns: + The callable date converter. + """ + + for prop in priority: + if props.get(prop): + return _jclass_converter(props[prop]) + + return default_converter + + +def convert_date_props( + props: dict[str, Any], + simple_date_props: set[str], + list_date_props: set[str], + callable_date_props: set[str], + callable_date_converter: Callable[[dict[str, Any]], Callable[[Date], Any]], +) -> None: + """ + Convert date props to Java date types in place. + + Args: + props: The props passed to the component. + simple_date_props: A set of simple date keys to convert. The prop value should be a single Date. + list_date_props: A set of list date keys to convert. The prop value should be a list of Dates. + callable_date_props: A set of callable date keys to convert. + The prop value should be a callable that takes a Date. + callable_date_converter: A callable that takes the props and + returns a date converter to be used for the callable date props. + + Returns: + The converted props. + """ + for key in simple_date_props: + if props.get(key) is not None: + props[key] = _convert_to_java_date(props[key]) + + for key in list_date_props: + if props.get(key) is not None: + if not isinstance(props[key], list): + raise TypeError(f"{key} must be a list of Dates") + props[key] = [_convert_to_java_date(date) for date in props[key]] + + converter = callable_date_converter(props) + + for key in callable_date_props: + if props.get(key) is not None: + if not callable(props[key]): + raise TypeError(f"{key} must be a callable") + props[key] = _wrap_date_callable(props[key], converter) diff --git a/plugins/ui/src/deephaven/ui/components/__init__.py b/plugins/ui/src/deephaven/ui/components/__init__.py index f0678f892..d18981f10 100644 --- a/plugins/ui/src/deephaven/ui/components/__init__.py +++ b/plugins/ui/src/deephaven/ui/components/__init__.py @@ -14,6 +14,7 @@ from .list_view import list_view from .list_action_group import list_action_group from .list_action_menu import list_action_menu +from .date_picker import date_picker from . import html @@ -28,6 +29,7 @@ "content", "contextual_help", "dashboard", + "date_picker", "flex", "form", "fragment", diff --git a/plugins/ui/src/deephaven/ui/components/date_picker.py b/plugins/ui/src/deephaven/ui/components/date_picker.py index ed57713f7..926616d57 100644 --- a/plugins/ui/src/deephaven/ui/components/date_picker.py +++ b/plugins/ui/src/deephaven/ui/components/date_picker.py @@ -1,139 +1,33 @@ from __future__ import annotations from typing import Any, Sequence, Callable - -from deephaven.dtypes import Instant, ZonedDateTime, LocalDate -from deephaven.time import to_j_instant, to_j_zdt, to_j_local_date - -from ..elements import Element -from .._internal.utils import create_props, wrap_callable +from deephaven.time import to_j_instant +from ..elements import Element, BaseElement +from .._internal.utils import ( + create_props, + convert_date_props, + prioritized_callable_date_converter, +) from ..types import Date, Granularity DatePickerElement = Element +# All the props that can be date types +_SIMPLE_DATE_PROPS = { + "placeholder_value", + "value", + "default_value", + "min_value", + "max_value", +} +_LIST_DATE_PROPS = {"unavailable_values"} +_CALLABLE_DATE_PROPS = {"on_change"} -def convert_to_java_date( - date: Date, -) -> Instant | ZonedDateTime | LocalDate: # type: ignore - """ - Convert a Date to a Java date type. - In order of preference, tries to convert to Instant, ZonedDateTime, and LocalDate. - If none of these work, raises a TypeError. - - Args: - date: The date to convert to a Java date type. - - Returns: - The Java date type. - """ - try: - return to_j_instant(date) - except TypeError: - # ignore, try next - pass - - try: - return to_j_zdt(date) - except TypeError: - # ignore, try next - pass - - try: - return to_j_local_date(date) - except TypeError: - raise TypeError( - f"Could not convert {date} to one of Instant, ZonedDateTime, or LocalDate." - ) - - -def date_converter( - props: dict[str, Any], -) -> Callable[[Date], Instant | ZonedDateTime | LocalDate]: # type: ignore - """ - Go through "value", "default_value", and "placeholder_value" in props to - determine the type of date converter to use. - If none of these are present, defaults to Instant. - This is used to convert callback arguments to Java date types. - - Args: - props: The props to check for date types. - - Returns: - The date converter. - """ - converters = { - Instant: to_j_instant, - ZonedDateTime: to_j_zdt, - LocalDate: to_j_local_date, - } - - if "value" in props: - return converters[type(props["value"])] - elif "default_value" in props: - return converters[type(props["default_value"])] - elif "placeholder_value" in props: - return converters[type(props["placeholder_value"])] - - return to_j_instant - - -def wrap_date_callable( - date_callable: Callable[[Date], None], - converter: Callable[[Date], Any], -) -> Callable[[Date], None]: - """ - Wrap a callable to convert the Date argument to a Java date type. - This maintains the original callable signature so that the Date argument can be dropped. - - Args: - date_callable: The callable to wrap. - converter: The date converter to use. - - Returns: - The wrapped callable. - """ - return lambda date: wrap_callable(date_callable)(converter(date)) - - -def convert_date_props( - props: dict[str, Any], - simple_date_props: set[str], - list_date_props: set[str], - callable_date_props: set[str], - converter: Callable[[Date], Any], -) -> None: - """ - Convert date props to Java date types in place. - - Args: - props: The props passed to the component. - simple_date_props: A set of simple date keys to convert. The prop value should be a single Date. - list_date_props: A set of list date keys to convert. The prop value should be a list of Dates. - callable_date_props: A set of callable date keys to convert. - The prop value should be a callable that takes a Date. - converter: The date converter to use. - - Returns: - The converted props. - """ - for key in simple_date_props: - if key in props: - props[key] = convert_to_java_date(props[key]) - - for key in list_date_props: - if key in props: - if not isinstance(props[key], list): - raise TypeError(f"{key} must be a list of Dates") - props[key] = [convert_to_java_date(date) for date in props[key]] +# The priority of the date props to determine the format of the date passed to the callable date props +_DATE_PROPS_PRIORITY = ["value", "default_value", "placeholder_value"] - for key in callable_date_props: - if key in props: - if not callable(props[key]): - raise TypeError(f"{key} must be a callable") - props[key] = wrap_date_callable(props[key], converter) - -def convert_date_picker_props( +def _convert_date_picker_props( props: dict[str, Any], ) -> dict[str, Any]: """ @@ -145,20 +39,17 @@ def convert_date_picker_props( Returns: The converted props. """ - simple_date_props = { - "placeholder_value", - "value", - "default_value", - "min_value", - "max_value", - } - list_date_props = {"unavailable_values"} - callable_date_props = {"on_change"} - - converter = date_converter(props) + + callable_date_converter = prioritized_callable_date_converter( + props, _DATE_PROPS_PRIORITY, to_j_instant + ) convert_date_props( - props, simple_date_props, list_date_props, callable_date_props, converter + props, + _SIMPLE_DATE_PROPS, + _LIST_DATE_PROPS, + _CALLABLE_DATE_PROPS, + callable_date_converter, ) return props @@ -174,7 +65,7 @@ def date_picker( granularity: Granularity | None = None, on_change: Callable[[Date], None] | None = None, **props: Any, -) -> DatePickerElement | None: +) -> DatePickerElement: """ @@ -185,8 +76,6 @@ def date_picker( """ _, props = create_props(locals()) - convert_date_picker_props(props) - - print(props) + _convert_date_picker_props(props) - return None # BaseElement("deephaven.ui.components.DatePicker", **props) + return BaseElement("deephaven.ui.components.DatePicker", **props) From a8dd9aceaaf3b18964f9a35124850a1ff8487ea0 Mon Sep 17 00:00:00 2001 From: Joe Numainville Date: Wed, 10 Apr 2024 13:46:50 -0500 Subject: [PATCH 3/9] docs --- .../ui/src/deephaven/ui/_internal/utils.py | 27 ++- .../deephaven/ui/components/date_picker.py | 204 +++++++++++++++++- .../ui/components/spectrum/__init__.py | 1 + plugins/ui/test/deephaven/ui/test_utils.py | 1 + 4 files changed, 218 insertions(+), 15 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/_internal/utils.py b/plugins/ui/src/deephaven/ui/_internal/utils.py index e29ce21d3..98746041b 100644 --- a/plugins/ui/src/deephaven/ui/_internal/utils.py +++ b/plugins/ui/src/deephaven/ui/_internal/utils.py @@ -249,6 +249,19 @@ def _convert_to_java_date( ) +def get_jclass_name(value: Any) -> str: + """ + Get the name of the Java class of the value. + + Args: + value: The value to get the Java class name of. + + Returns: + The name of the Java class of the value. + """ + return str(value.jclass)[6:] + + def _jclass_converter( value: Instant | ZonedDateTime | LocalDate, # type: ignore ) -> Callable[[Date], Any]: @@ -261,7 +274,7 @@ def _jclass_converter( Returns: The converter for the Java date type. """ - return _CONVERTERS[str(value.jclass)[6:]] + return _CONVERTERS[get_jclass_name(value)] def _wrap_date_callable( @@ -282,7 +295,7 @@ def _wrap_date_callable( return lambda date: wrap_callable(date_callable)(converter(date)) -def prioritized_callable_date_converter( +def _prioritized_callable_converter( props: dict[str, Any], priority: Sequence[str], default_converter: Callable[[Date], Any], @@ -312,7 +325,8 @@ def convert_date_props( simple_date_props: set[str], list_date_props: set[str], callable_date_props: set[str], - callable_date_converter: Callable[[dict[str, Any]], Callable[[Date], Any]], + priority: Sequence[str], + default_converter: Callable[[Date], Any] = to_j_instant, ) -> None: """ Convert date props to Java date types in place. @@ -323,8 +337,8 @@ def convert_date_props( list_date_props: A set of list date keys to convert. The prop value should be a list of Dates. callable_date_props: A set of callable date keys to convert. The prop value should be a callable that takes a Date. - callable_date_converter: A callable that takes the props and - returns a date converter to be used for the callable date props. + priority: The priority of the props to check. + default_converter: The default converter to use if none of the priority props are present. Returns: The converted props. @@ -339,7 +353,8 @@ def convert_date_props( raise TypeError(f"{key} must be a list of Dates") props[key] = [_convert_to_java_date(date) for date in props[key]] - converter = callable_date_converter(props) + # the simple props must be converted before this to simplify the callable conversion + converter = _prioritized_callable_converter(props, priority, default_converter) for key in callable_date_props: if props.get(key) is not None: diff --git a/plugins/ui/src/deephaven/ui/components/date_picker.py b/plugins/ui/src/deephaven/ui/components/date_picker.py index 926616d57..bac84d22c 100644 --- a/plugins/ui/src/deephaven/ui/components/date_picker.py +++ b/plugins/ui/src/deephaven/ui/components/date_picker.py @@ -1,12 +1,30 @@ from __future__ import annotations from typing import Any, Sequence, Callable -from deephaven.time import to_j_instant + +from .spectrum import ( + FocusEventCallable, + KeyboardEventCallable, + LayoutFlex, + Number, + DimensionValue, + AlignSelf, + JustifySelf, + Position, + AriaPressed, + CSSProperties, + LabelPosition, + Alignment, + ValidationBehavior, + NecessityIndicator, + ValidationState, + PageBehavior, + HourCycle, +) from ..elements import Element, BaseElement from .._internal.utils import ( create_props, convert_date_props, - prioritized_callable_date_converter, ) from ..types import Date, Granularity @@ -40,16 +58,12 @@ def _convert_date_picker_props( The converted props. """ - callable_date_converter = prioritized_callable_date_converter( - props, _DATE_PROPS_PRIORITY, to_j_instant - ) - convert_date_props( props, _SIMPLE_DATE_PROPS, _LIST_DATE_PROPS, _CALLABLE_DATE_PROPS, - callable_date_converter, + _DATE_PROPS_PRIORITY, ) return props @@ -63,16 +77,188 @@ def date_picker( max_value: Date | None = None, unavailable_values: Sequence[Date] | None = None, granularity: Granularity | None = None, + page_behavior: PageBehavior | None = None, + hour_cycle: HourCycle | None = None, + hide_time_zone: bool = False, + should_force_leading_zeros: bool | None = None, + is_disabled: bool | None = None, + is_read_only: bool | None = None, + is_required: bool | None = None, + validation_behavior: ValidationBehavior | None = None, + auto_focus: bool | None = None, + label: Element | None = None, + description: Element | None = None, + error_message: Element | None = None, + is_open: bool | None = None, + default_open: bool | None = None, + name: str | None = None, + max_visible_months: int | None = None, + should_flip: bool | None = None, + is_quiet: bool | None = None, + show_format_help_text: bool | None = None, + label_position: LabelPosition | None = None, + label_align: Alignment | None = None, + necessity_indicator: NecessityIndicator | None = None, + contextual_help: Element | None = None, + validation_state: ValidationState | None = None, + on_focus: FocusEventCallable | None = None, + on_blur: FocusEventCallable | None = None, + on_focus_change: Callable[[bool], None] | None = None, + on_key_down: KeyboardEventCallable | None = None, + on_key_up: KeyboardEventCallable | None = None, + on_open_change: Callable[[bool], None] | None = None, on_change: Callable[[Date], None] | None = None, - **props: Any, + flex: LayoutFlex | None = None, + flex_grow: Number | None = None, + flex_shrink: Number | None = None, + flex_basis: DimensionValue | None = None, + align_self: AlignSelf | None = None, + justify_self: JustifySelf | None = None, + order: Number | None = None, + grid_area: str | None = None, + grid_row: str | None = None, + grid_row_start: str | None = None, + grid_row_end: str | None = None, + grid_column: str | None = None, + grid_column_start: str | None = None, + grid_column_end: str | None = None, + margin: DimensionValue | None = None, + margin_top: DimensionValue | None = None, + margin_bottom: DimensionValue | None = None, + margin_start: DimensionValue | None = None, + margin_end: DimensionValue | None = None, + margin_x: DimensionValue | None = None, + margin_y: DimensionValue | None = None, + width: DimensionValue | None = None, + height: DimensionValue | None = None, + min_width: DimensionValue | None = None, + min_height: DimensionValue | None = None, + max_width: DimensionValue | None = None, + max_height: DimensionValue | None = None, + position: Position | None = None, + top: DimensionValue | None = None, + bottom: DimensionValue | None = None, + start: DimensionValue | None = None, + end: DimensionValue | None = None, + left: DimensionValue | None = None, + right: DimensionValue | None = None, + z_index: Number | None = None, + is_hidden: bool | None = None, + id: str | None = None, + aria_label: str | None = None, + aria_labelledby: str | None = None, + aria_describedby: str | None = None, + aria_pressed: AriaPressed | None = None, + aria_details: str | None = None, + UNSAFE_class_name: str | None = None, + UNSAFE_style: CSSProperties | None = None, ) -> DatePickerElement: """ + A date picker allows the user to select a date. Args: - + placeholder_value: A placeholder date that influences the format of the + placeholder shown when no value is selected. + Defaults to today at midnight in the user's timezone. + value: The current value (controlled). + default_value: The default value (uncontrolled). + min_value: The minimum allowed date that a user may select. + max_value: The maximum allowed date that a user may select. + unavailable_values: A list of dates that cannot be selected. + granularity: Determines the smallest unit that is displayed in the date picker. + By default, this is `"DAY"` for `LocalDate`, and `"SECOND"` otherwise. + page_behavior: Controls the behavior of paging. Pagination either works by + advancing the visible page by visibleDuration (default) + or one unit of visibleDuration. + hour_cycle: Whether to display the time in 12 or 24 hour format. + By default, this is determined by the user's locale. + hide_time_zone: Whether to hide the time zone abbreviation. + should_force_leading_zeros: Whether to always show leading zeros in the + month, day, and hour fields. + By default, this is determined by the user's locale. + is_disabled: Whether the input is disabled. + is_read_only: Whether the input can be selected but not changed by the user. + is_required: Whether user input is required on the input before form submission. + validation_behavior: Whether to use native HTML form validation to prevent form + submission when the value is missing or invalid, + or mark the field as required or invalid via ARIA. + auto_focus: Whether the element should receive focus on render. + label: The content to display as the label. + description: A description for the field. + Provides a hint such as specific requirements for what to choose. + error_message: An error message for the field. + is_open: Whether the overlay is open by default (controlled). + default_open: Whether the overlay is open by default (uncontrolled). + name: The name of the input element, used when submitting an HTML form. + max_visible_months: The maximum number of months to display at + once in the calendar popover, if screen space permits. + should_flip: Whether the calendar popover should automatically flip direction + when space is limited. + is_quiet: Whether the date picker should be displayed with a quiet style. + show_format_help_text: Whether to show the localized date format as help + text below the field. + label_position: The label's overall position relative to the element it is labeling. + label_align: The label's horizontal alignment relative to the element it is labeling. + necessity_indicator: Whether the required state should be shown as an icon or text. + contextual_help: A ContextualHelp element to place next to the label. + validation_state: Whether the input should display its "valid" or "invalid" visual styling. + on_focus: Function called when the button receives focus. + on_blur: Function called when the button loses focus. + on_focus_change: Function called when the focus state changes. + on_key_down: Function called when a key is pressed. + on_key_up: Function called when a key is released. + on_open_change: Handler that is called when the overlay's open state changes. + on_change: Handler that is called when the value changes. + The exact `Date` type will be the same as the type passed to + `value`, `default_value` or `placeholder_value`, in that order of precedence. + flex: When used in a flex layout, specifies how the element will grow or shrink to fit the space available. + flex_grow: When used in a flex layout, specifies how much the element will grow to fit the space available. + flex_shrink: When used in a flex layout, specifies how much the element will shrink to fit the space available. + flex_basis: When used in a flex layout, specifies the initial size of the element. + align_self: Overrides the align_items property of a flex or grid container. + justify_self: Specifies how the element is justified inside a flex or grid container. + order: The layout order for the element within a flex or grid container. + grid_area: The name of the grid area to place the element in. + grid_row: The name of the grid row to place the element in. + grid_row_start: The name of the grid row to start the element in. + grid_row_end: The name of the grid row to end the element in. + grid_column: The name of the grid column to place the element in. + grid_column_start: The name of the grid column to start the element in. + grid_column_end: The name of the grid column to end the element in. + margin: The margin to apply around the element. + margin_top: The margin to apply above the element. + margin_bottom: The margin to apply below the element. + margin_start: The margin to apply before the element. + margin_end: The margin to apply after the element. + margin_x: The margin to apply to the left and right of the element. + margin_y: The margin to apply to the top and bottom of the element. + width: The width of the element. + height: The height of the element. + min_width: The minimum width of the element. + min_height: The minimum height of the element. + max_width: The maximum width of the element. + max_height: The maximum height of the element. + position: Specifies how the element is positioned. + top: The distance from the top of the containing element. + bottom: The distance from the bottom of the containing element. + start: The distance from the start of the containing element. + end: The distance from the end of the containing element. + left: The distance from the left of the containing element. + right: The distance from the right of the containing element. + z_index: The stack order of the element. + is_hidden: Whether the element is hidden. + id: A unique identifier for the element. + aria_label: The label for the element. + aria_labelledby: The id of the element that labels the element. + aria_describedby: The id of the element that describes the element. + aria_pressed: Whether the element is pressed. + aria_details: The details for the element. + UNSAFE_class_name: A CSS class to apply to the element. + UNSAFE_style: A CSS style to apply to the element. Returns: + The date picker element. """ _, props = create_props(locals()) diff --git a/plugins/ui/src/deephaven/ui/components/spectrum/__init__.py b/plugins/ui/src/deephaven/ui/components/spectrum/__init__.py index becbde521..d48c9d9ed 100644 --- a/plugins/ui/src/deephaven/ui/components/spectrum/__init__.py +++ b/plugins/ui/src/deephaven/ui/components/spectrum/__init__.py @@ -4,3 +4,4 @@ from .button import * from .text_field import * from .flex import * +from .date_picker import * diff --git a/plugins/ui/test/deephaven/ui/test_utils.py b/plugins/ui/test/deephaven/ui/test_utils.py index f67f2bb80..4b853f035 100644 --- a/plugins/ui/test/deephaven/ui/test_utils.py +++ b/plugins/ui/test/deephaven/ui/test_utils.py @@ -1,4 +1,5 @@ import unittest + from .BaseTest import BaseTestCase From d2086b667bca13f2d59e04a1e98c92f17f1a1bde Mon Sep 17 00:00:00 2001 From: Joe Numainville Date: Wed, 10 Apr 2024 14:05:27 -0500 Subject: [PATCH 4/9] remove line --- plugins/ui/test/deephaven/ui/test_utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/ui/test/deephaven/ui/test_utils.py b/plugins/ui/test/deephaven/ui/test_utils.py index 4b853f035..f67f2bb80 100644 --- a/plugins/ui/test/deephaven/ui/test_utils.py +++ b/plugins/ui/test/deephaven/ui/test_utils.py @@ -1,5 +1,4 @@ import unittest - from .BaseTest import BaseTestCase From c6c2951beb3df10bc803b6fbc507d885d8f80af1 Mon Sep 17 00:00:00 2001 From: Joe Numainville Date: Wed, 10 Apr 2024 14:57:44 -0500 Subject: [PATCH 5/9] fix props --- .../ui/src/deephaven/ui/_internal/utils.py | 2 +- .../ui/components/spectrum/date_picker.py | 8 ++ .../ui/test/deephaven/ui/test_date_picker.py | 79 +++++++++++++++++++ plugins/ui/test/deephaven/ui/test_utils.py | 44 +++++++++++ 4 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 plugins/ui/src/deephaven/ui/components/spectrum/date_picker.py create mode 100644 plugins/ui/test/deephaven/ui/test_date_picker.py diff --git a/plugins/ui/src/deephaven/ui/_internal/utils.py b/plugins/ui/src/deephaven/ui/_internal/utils.py index 98746041b..53d012517 100644 --- a/plugins/ui/src/deephaven/ui/_internal/utils.py +++ b/plugins/ui/src/deephaven/ui/_internal/utils.py @@ -210,7 +210,7 @@ def create_props(args: dict[str, Any]) -> tuple[tuple[Any], dict[str, Any]]: Returns: A tuple of children and props """ - children, props = args.pop("children"), args.pop("props") + children, props = args.pop("children", tuple()), args.pop("props", {}) props.update(args) return children, props diff --git a/plugins/ui/src/deephaven/ui/components/spectrum/date_picker.py b/plugins/ui/src/deephaven/ui/components/spectrum/date_picker.py new file mode 100644 index 000000000..3c8be378d --- /dev/null +++ b/plugins/ui/src/deephaven/ui/components/spectrum/date_picker.py @@ -0,0 +1,8 @@ +from typing import Literal + +PageBehavior = Literal["single", "visible"] +HourCycle = Literal[12, 24] +ValidationBehavior = Literal["aria", "native"] +Alignment = Literal["start", "end"] +NecessityIndicator = Literal["label", "icon"] +ValidationState = Literal["valid", "invalid"] diff --git a/plugins/ui/test/deephaven/ui/test_date_picker.py b/plugins/ui/test/deephaven/ui/test_date_picker.py new file mode 100644 index 000000000..2055e81e1 --- /dev/null +++ b/plugins/ui/test/deephaven/ui/test_date_picker.py @@ -0,0 +1,79 @@ +import unittest + +from .BaseTest import BaseTestCase + + +class DatePickerTest(BaseTestCase): + def test_convert_date_props(self): + from deephaven.time import to_j_instant, to_j_zdt, to_j_local_date + from deephaven.ui.components.date_picker import _convert_date_picker_props + from deephaven.ui._internal.utils import get_jclass_name + + def verify_is_local_date(date): + self.assertEqual(get_jclass_name(date), "java.time.LocalDate") + + def verify_is_instant(date): + self.assertEqual(get_jclass_name(date), "java.time.Instant") + + def verify_is_zdt(date): + self.assertEqual(get_jclass_name(date), "java.time.ZonedDateTime") + + def empty_on_change(): + pass + + props1 = { + "placeholder_value": "2021-01-01", + "value": "2021-01-01 UTC", + "default_value": "2021-01-01 ET", + "unavailable_dates": [to_j_instant("2021-01-01 UTC")], + "min_value": to_j_zdt("2021-01-01 ET"), + "max_value": to_j_local_date("2021-01-01"), + } + + props2 = { + "value": to_j_local_date("2021-01-01"), + "default_value": to_j_zdt("2021-01-01 ET"), + "placeholder_value": to_j_instant("2021-01-01 UTC"), + "on_change": verify_is_local_date, + } + + props3 = { + "default_value": to_j_instant("2021-01-01 UTC"), + "placeholder_value": to_j_zdt("2021-01-01 ET"), + "on_change": verify_is_instant, + } + + props4 = { + "placeholder_value": to_j_zdt("2021-01-01 ET"), + "on_change": verify_is_zdt, + } + + props5 = {"on_change": verify_is_instant} + + props6 = {"on_change": empty_on_change} + + _convert_date_picker_props(props1) + _convert_date_picker_props(props2) + _convert_date_picker_props(props3) + _convert_date_picker_props(props4) + _convert_date_picker_props(props5) + _convert_date_picker_props(props6) + + verify_is_local_date(props1["max_value"]) + verify_is_zdt(props1["min_value"]) + verify_is_instant(props1["unavailable_dates"][0]) + verify_is_instant(props1["value"]) + verify_is_instant(props1["default_value"]) + verify_is_local_date(props1["placeholder_value"]) + + props2["on_change"]("2021-01-01") + props3["on_change"]("2021-01-01 UTC") + props4["on_change"]("2021-01-01 ET") + props5["on_change"]("2021-01-01 UTC") + + # pass an Instant but it should be dropped with no error + props6["on_change"]("2021-01-01 UTC") + + +if __name__ == "__main__": + unittest.main() diff --git a/plugins/ui/test/deephaven/ui/test_utils.py b/plugins/ui/test/deephaven/ui/test_utils.py index f67f2bb80..d98bca7e0 100644 --- a/plugins/ui/test/deephaven/ui/test_utils.py +++ b/plugins/ui/test/deephaven/ui/test_utils.py @@ -217,6 +217,50 @@ def test_func_with_all_args(a, /, b, *args, c=1, **kwargs): # Test that wrapping a function without a signature doesn't throw an error wrapped = wrap_callable(print) + def test_create_props(self): + from deephaven.ui._internal.utils import create_props + + children1, props1 = create_props( + { + "foo": "bar", + "baz": 42, + "fizz": "buzz", + } + ) + + self.assertEqual(children1, tuple()) + self.assertDictEqual( + props1, + { + "foo": "bar", + "baz": 42, + "fizz": "buzz", + }, + ) + + children2, props2 = create_props( + { + "children": ["item1", "item2"], + "test": "value", + "props": { + "foo": "bar", + "baz": 42, + "fizz": "buzz", + }, + } + ) + + self.assertEqual(children2, ["item1", "item2"]) + self.assertDictEqual( + props2, + { + "foo": "bar", + "baz": 42, + "fizz": "buzz", + "test": "value", + }, + ) + if __name__ == "__main__": unittest.main() From 314fa9dc6474a8273cb0c422d34cec08a62e2b92 Mon Sep 17 00:00:00 2001 From: Joe Numainville Date: Thu, 11 Apr 2024 12:20:18 -0500 Subject: [PATCH 6/9] fixed types --- plugins/ui/src/deephaven/ui/_internal/utils.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/_internal/utils.py b/plugins/ui/src/deephaven/ui/_internal/utils.py index 53d012517..2b37cbd37 100644 --- a/plugins/ui/src/deephaven/ui/_internal/utils.py +++ b/plugins/ui/src/deephaven/ui/_internal/utils.py @@ -9,12 +9,6 @@ from ..types import Date -DateConverter = ( - Callable[[Date], Instant] # type: ignore - | Callable[[Date], ZonedDateTime] # type: ignore - | Callable[[Date], LocalDate] # type: ignore -) - _UNSAFE_PREFIX = "UNSAFE_" _ARIA_PREFIX = "aria_" _ARIA_PREFIX_REPLACEMENT = "aria-" @@ -299,7 +293,7 @@ def _prioritized_callable_converter( props: dict[str, Any], priority: Sequence[str], default_converter: Callable[[Date], Any], -) -> DateConverter: +) -> Callable[[Date], Any]: """ Get a callable date converter based on the priority of the props. If none of the priority props are present, uses the default converter. From 9a24624bd1bcc0226c3f0a3ee628a5772cc51ab2 Mon Sep 17 00:00:00 2001 From: Joe Numainville Date: Thu, 11 Apr 2024 16:55:23 -0500 Subject: [PATCH 7/9] adding memo --- .../ui/src/deephaven/ui/_internal/utils.py | 29 ++++++++++++++----- .../deephaven/ui/components/date_picker.py | 8 ++++- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/_internal/utils.py b/plugins/ui/src/deephaven/ui/_internal/utils.py index 2b37cbd37..1c42f935c 100644 --- a/plugins/ui/src/deephaven/ui/_internal/utils.py +++ b/plugins/ui/src/deephaven/ui/_internal/utils.py @@ -314,10 +314,30 @@ def _prioritized_callable_converter( return default_converter +def convert_list_prop( + key: str, + value: list[Date], +) -> list[Instant | ZonedDateTime | LocalDate]: # type: ignore + """ + Convert a list of Dates to Java date types. + + Args: + key: The key of the prop. + value: A list of Dates to convert to Java date types. + + Returns: + The list of Java date types. + """ + if value is not None: + if not isinstance(value, list): + raise TypeError(f"{key} must be a list of Dates") + return [_convert_to_java_date(date) for date in value] + return [] + + def convert_date_props( props: dict[str, Any], simple_date_props: set[str], - list_date_props: set[str], callable_date_props: set[str], priority: Sequence[str], default_converter: Callable[[Date], Any] = to_j_instant, @@ -328,7 +348,6 @@ def convert_date_props( Args: props: The props passed to the component. simple_date_props: A set of simple date keys to convert. The prop value should be a single Date. - list_date_props: A set of list date keys to convert. The prop value should be a list of Dates. callable_date_props: A set of callable date keys to convert. The prop value should be a callable that takes a Date. priority: The priority of the props to check. @@ -341,12 +360,6 @@ def convert_date_props( if props.get(key) is not None: props[key] = _convert_to_java_date(props[key]) - for key in list_date_props: - if props.get(key) is not None: - if not isinstance(props[key], list): - raise TypeError(f"{key} must be a list of Dates") - props[key] = [_convert_to_java_date(date) for date in props[key]] - # the simple props must be converted before this to simplify the callable conversion converter = _prioritized_callable_converter(props, priority, default_converter) diff --git a/plugins/ui/src/deephaven/ui/components/date_picker.py b/plugins/ui/src/deephaven/ui/components/date_picker.py index bac84d22c..98160cf42 100644 --- a/plugins/ui/src/deephaven/ui/components/date_picker.py +++ b/plugins/ui/src/deephaven/ui/components/date_picker.py @@ -21,10 +21,12 @@ PageBehavior, HourCycle, ) +from ..hooks import use_memo from ..elements import Element, BaseElement from .._internal.utils import ( create_props, convert_date_props, + convert_list_prop, ) from ..types import Date, Granularity @@ -61,7 +63,6 @@ def _convert_date_picker_props( convert_date_props( props, _SIMPLE_DATE_PROPS, - _LIST_DATE_PROPS, _CALLABLE_DATE_PROPS, _DATE_PROPS_PRIORITY, ) @@ -264,4 +265,9 @@ def date_picker( _convert_date_picker_props(props) + props["unavailable_values"] = use_memo( + lambda: convert_list_prop("unavailable_values", props["unavailable_values"]), + [unavailable_values], + ) + return BaseElement("deephaven.ui.components.DatePicker", **props) From 1ded93fdce47423e6d1633e35df86ff62a7caeeb Mon Sep 17 00:00:00 2001 From: Joe Numainville Date: Fri, 12 Apr 2024 10:37:27 -0500 Subject: [PATCH 8/9] comments --- .../ui/src/deephaven/ui/_internal/utils.py | 51 ++++++++++++++----- .../ui/test/deephaven/ui/test_date_picker.py | 13 ++++- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/_internal/utils.py b/plugins/ui/src/deephaven/ui/_internal/utils.py index 1c42f935c..d7d0763c1 100644 --- a/plugins/ui/src/deephaven/ui/_internal/utils.py +++ b/plugins/ui/src/deephaven/ui/_internal/utils.py @@ -289,14 +289,35 @@ def _wrap_date_callable( return lambda date: wrap_callable(date_callable)(converter(date)) +def _get_first_set_key(props: dict[str, Any], sequence: Sequence[str]) -> str | None: + """ + Of the keys in sequence, get the first key that has a non-None value in props. + If none of the keys have a non-None value, return None. + + Args: + props: The props to check for non-None values. + sequence: The sequence to check. + + Returns: + The first non-None prop, or None if all props are None. + """ + for key in sequence: + if props.get(key) is not None: + return key + return None + + def _prioritized_callable_converter( props: dict[str, Any], priority: Sequence[str], default_converter: Callable[[Date], Any], ) -> Callable[[Date], Any]: """ - Get a callable date converter based on the priority of the props. - If none of the priority props are present, uses the default converter. + Get a callable date converter based on the type of the first non-None prop set. + Checks the props in the order provided by the `priority` sequence. + All the props in `priority` should be Java date types already. + We do this so conversion so that the type returned on callbacks matches the type passed in by the user. + If none of the props in `priority` are present, returns the default converter. Args: props: The props passed to the component. @@ -307,17 +328,18 @@ def _prioritized_callable_converter( The callable date converter. """ - for prop in priority: - if props.get(prop): - return _jclass_converter(props[prop]) - - return default_converter + first_set_key = _get_first_set_key(props, priority) + return ( + _jclass_converter(props[first_set_key]) + if first_set_key is not None + else default_converter + ) def convert_list_prop( key: str, - value: list[Date], -) -> list[Instant | ZonedDateTime | LocalDate]: # type: ignore + value: list[Date] | None, +) -> list[Instant | ZonedDateTime | LocalDate] | None: # type: ignore """ Convert a list of Dates to Java date types. @@ -328,11 +350,12 @@ def convert_list_prop( Returns: The list of Java date types. """ - if value is not None: - if not isinstance(value, list): - raise TypeError(f"{key} must be a list of Dates") - return [_convert_to_java_date(date) for date in value] - return [] + if value is None: + return None + + if not isinstance(value, list): + raise TypeError(f"{key} must be a list of Dates") + return [_convert_to_java_date(date) for date in value] def convert_date_props( diff --git a/plugins/ui/test/deephaven/ui/test_date_picker.py b/plugins/ui/test/deephaven/ui/test_date_picker.py index 2055e81e1..d7a2d9624 100644 --- a/plugins/ui/test/deephaven/ui/test_date_picker.py +++ b/plugins/ui/test/deephaven/ui/test_date_picker.py @@ -7,7 +7,7 @@ class DatePickerTest(BaseTestCase): def test_convert_date_props(self): from deephaven.time import to_j_instant, to_j_zdt, to_j_local_date from deephaven.ui.components.date_picker import _convert_date_picker_props - from deephaven.ui._internal.utils import get_jclass_name + from deephaven.ui._internal.utils import get_jclass_name, convert_list_prop def verify_is_local_date(date): self.assertEqual(get_jclass_name(date), "java.time.LocalDate") @@ -25,7 +25,7 @@ def empty_on_change(): "placeholder_value": "2021-01-01", "value": "2021-01-01 UTC", "default_value": "2021-01-01 ET", - "unavailable_dates": [to_j_instant("2021-01-01 UTC")], + "unavailable_dates": [to_j_instant("2021-01-01 UTC"), "2021-01-01"], "min_value": to_j_zdt("2021-01-01 ET"), "max_value": to_j_local_date("2021-01-01"), } @@ -35,6 +35,7 @@ def empty_on_change(): "default_value": to_j_zdt("2021-01-01 ET"), "placeholder_value": to_j_instant("2021-01-01 UTC"), "on_change": verify_is_local_date, + "unavailable_dates": None, } props3 = { @@ -53,7 +54,13 @@ def empty_on_change(): props6 = {"on_change": empty_on_change} _convert_date_picker_props(props1) + props1["unavailable_dates"] = convert_list_prop( + "unavailable_dates", props1["unavailable_dates"] + ) _convert_date_picker_props(props2) + props2["unavailable_dates"] = convert_list_prop( + "unavailable_dates", props2["unavailable_dates"] + ) _convert_date_picker_props(props3) _convert_date_picker_props(props4) _convert_date_picker_props(props5) @@ -62,11 +69,13 @@ def empty_on_change(): verify_is_local_date(props1["max_value"]) verify_is_zdt(props1["min_value"]) verify_is_instant(props1["unavailable_dates"][0]) + verify_is_local_date(props1["unavailable_dates"][1]) verify_is_instant(props1["value"]) verify_is_instant(props1["default_value"]) verify_is_local_date(props1["placeholder_value"]) props2["on_change"]("2021-01-01") + self.assertIsNone(props2["unavailable_dates"]) props3["on_change"]("2021-01-01 UTC") props4["on_change"]("2021-01-01 ET") props5["on_change"]("2021-01-01 UTC") From 6c217bc81a53ab3f7a341eef12f04cd8b02b4148 Mon Sep 17 00:00:00 2001 From: Joe Numainville Date: Fri, 12 Apr 2024 16:23:08 -0500 Subject: [PATCH 9/9] fixed types --- plugins/ui/src/deephaven/ui/_internal/utils.py | 15 +++++++-------- plugins/ui/src/deephaven/ui/types/types.py | 14 +++++++++----- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/plugins/ui/src/deephaven/ui/_internal/utils.py b/plugins/ui/src/deephaven/ui/_internal/utils.py index d7d0763c1..3ee0fe706 100644 --- a/plugins/ui/src/deephaven/ui/_internal/utils.py +++ b/plugins/ui/src/deephaven/ui/_internal/utils.py @@ -4,10 +4,9 @@ from inspect import signature import sys from functools import partial -from deephaven.dtypes import Instant, ZonedDateTime, LocalDate from deephaven.time import to_j_instant, to_j_zdt, to_j_local_date -from ..types import Date +from ..types import Date, JavaDate _UNSAFE_PREFIX = "UNSAFE_" _ARIA_PREFIX = "aria_" @@ -211,7 +210,7 @@ def create_props(args: dict[str, Any]) -> tuple[tuple[Any], dict[str, Any]]: def _convert_to_java_date( date: Date, -) -> Instant | ZonedDateTime | LocalDate: # type: ignore +) -> JavaDate: """ Convert a Date to a Java date type. In order of preference, tries to convert to Instant, ZonedDateTime, and LocalDate. @@ -224,19 +223,19 @@ def _convert_to_java_date( The Java date type. """ try: - return to_j_instant(date) + return to_j_instant(date) # type: ignore except Exception: # ignore, try next pass try: - return to_j_zdt(date) + return to_j_zdt(date) # type: ignore except Exception: # ignore, try next pass try: - return to_j_local_date(date) + return to_j_local_date(date) # type: ignore except Exception: raise TypeError( f"Could not convert {date} to one of Instant, ZonedDateTime, or LocalDate." @@ -257,7 +256,7 @@ def get_jclass_name(value: Any) -> str: def _jclass_converter( - value: Instant | ZonedDateTime | LocalDate, # type: ignore + value: JavaDate, ) -> Callable[[Date], Any]: """ Get the converter for the Java date type. @@ -339,7 +338,7 @@ def _prioritized_callable_converter( def convert_list_prop( key: str, value: list[Date] | None, -) -> list[Instant | ZonedDateTime | LocalDate] | None: # type: ignore +) -> list[JavaDate] | None: """ Convert a list of Dates to Java date types. diff --git a/plugins/ui/src/deephaven/ui/types/types.py b/plugins/ui/src/deephaven/ui/types/types.py index 69db7a10e..9641d5aab 100644 --- a/plugins/ui/src/deephaven/ui/types/types.py +++ b/plugins/ui/src/deephaven/ui/types/types.py @@ -3,7 +3,7 @@ import numpy from typing import Any, Dict, Literal, Union, List, Tuple, Callable, TypedDict, Sequence from deephaven import SortDirection -from deephaven.dtypes import LocalDate, Instant, ZonedDateTime +from deephaven.dtypes import DType class CellData(TypedDict): @@ -110,9 +110,13 @@ class RowDataValue(CellData): Stringable = Union[str, int, float, bool] Key = Stringable ActionKey = Key +LocalDate = DType +Instant = DType +ZonedDateTime = DType +JavaDate = Union[LocalDate, Instant, ZonedDateTime] LocalDateConvertible = Union[ None, - LocalDate, # type: ignore + LocalDate, str, datetime.date, datetime.datetime, @@ -126,9 +130,9 @@ class RowDataValue(CellData): None, ZonedDateTime, str, datetime.datetime, numpy.datetime64, pandas.Timestamp # type: ignore ] Date = Union[ - Instant, # type: ignore - LocalDate, # type: ignore - ZonedDateTime, # type: ignore + Instant, + LocalDate, + ZonedDateTime, LocalDateConvertible, InstantConvertible, ZonedDateTimeConvertible,