From 490ae40a40a23bb0fc68ed0a78145e04917416e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Brand=C3=A9ho?= Date: Mon, 23 Oct 2023 13:24:11 +0200 Subject: [PATCH] Wrap Moment Component (#1994) * wip wrap moment * add some props to moment * fix typing for 3.8 * fix comment for props and add create method for moment-timezone --- reflex/components/__init__.py | 1 + reflex/components/component.py | 14 +-- reflex/components/datadisplay/__init__.py | 1 + reflex/components/datadisplay/moment.py | 110 ++++++++++++++++++++++ 4 files changed, 119 insertions(+), 7 deletions(-) create mode 100644 reflex/components/datadisplay/moment.py diff --git a/reflex/components/__init__.py b/reflex/components/__init__.py index b4f44619e4..bd098a2935 100644 --- a/reflex/components/__init__.py +++ b/reflex/components/__init__.py @@ -113,6 +113,7 @@ input_right_addon = InputRightAddon.create input_left_element = InputLeftElement.create input_right_element = InputRightElement.create +moment = Moment.create multi_select = MultiSelect.create multi_select_option = MultiSelectOption number_decrement_stepper = NumberDecrementStepper.create diff --git a/reflex/components/component.py b/reflex/components/component.py index b290d996eb..b9f4ec82e1 100644 --- a/reflex/components/component.py +++ b/reflex/components/component.py @@ -937,14 +937,14 @@ def wrapper(*children, **props) -> CustomComponent: class NoSSRComponent(Component): """A dynamic component that is not rendered on the server.""" - def _get_imports(self): - imports = {"next/dynamic": {ImportVar(tag="dynamic", is_default=True)}} + def _get_imports(self) -> imports.ImportDict: + dynamic_import = {"next/dynamic": {ImportVar(tag="dynamic", is_default=True)}} - return { - **imports, - self.library: {ImportVar(tag=None, render=False)}, - **self._get_dependencies_imports(), - } + return imports.merge_imports( + dynamic_import, + {self.library: {ImportVar(tag=None, render=False)}}, + self._get_dependencies_imports(), + ) def _get_dynamic_imports(self) -> str: opts_fragment = ", { ssr: false });" diff --git a/reflex/components/datadisplay/__init__.py b/reflex/components/datadisplay/__init__.py index 3d3a15545c..0de2cf40e2 100644 --- a/reflex/components/datadisplay/__init__.py +++ b/reflex/components/datadisplay/__init__.py @@ -6,6 +6,7 @@ from .divider import Divider from .keyboard_key import KeyboardKey from .list import List, ListItem, OrderedList, UnorderedList +from .moment import Moment from .stat import Stat, StatArrow, StatGroup, StatHelpText, StatLabel, StatNumber from .table import Table, TableCaption, TableContainer, Tbody, Td, Tfoot, Th, Thead, Tr from .tag import Tag, TagCloseButton, TagLabel, TagLeftIcon, TagRightIcon diff --git a/reflex/components/datadisplay/moment.py b/reflex/components/datadisplay/moment.py new file mode 100644 index 0000000000..9ae8381bc2 --- /dev/null +++ b/reflex/components/datadisplay/moment.py @@ -0,0 +1,110 @@ +"""Moment component for humanized date rendering.""" + +from typing import Any, Dict, List + +from reflex.components.component import Component, NoSSRComponent +from reflex.utils import imports +from reflex.vars import ImportVar, Var + + +class Moment(NoSSRComponent): + """The Moment component.""" + + tag: str = "Moment" + is_default = True + library: str = "react-moment" + lib_dependencies: List[str] = ["moment"] + + # How often the date update (how often time update / 0 to disable). + interval: Var[int] + + # Formats the date according to the given format string. + format: Var[str] + + # When formatting duration time, the largest-magnitude tokens are automatically trimmed when they have no value. + trim: Var[bool] + + # Use the parse attribute to tell moment how to parse the given date when non-standard. + parse: Var[str] + + # NOT IMPLEMENTED : + # add + # substract + + # Displays the date as the time from now, e.g. "5 minutes ago". + from_now: Var[bool] + + # Setting fromNowDuring will display the relative time as with fromNow but just during its value in milliseconds, after that format will be used instead. + from_now_during: Var[int] + + # Similar to fromNow, but gives the opposite interval. + to_now: Var[bool] + + # Adds a title attribute to the element with the complete date. + with_title: Var[bool] + + # How the title date is formatted when using the withTitle attribute. + title_format: Var[str] + + # Show the different between this date and the rendered child. + diff: Var[str] + + # Display the diff as decimal. + decimal: Var[bool] + + # Display the diff in given unit. + unit: Var[str] + + # Shows the duration (elapsed time) between two dates. duration property should be behind date property time-wise. + duration: Var[str] + + # The date to display (also work if passed as children). + date: Var[str] + + # Shows the duration (elapsed time) between now and the provided datetime. + duration_from_now: Var[bool] + + # Tells Moment to parse the given date value as a unix timestamp. + unix: Var[bool] + + # Outputs the result in local time. + local: Var[bool] + + # Display the date in the given timezone. + tz: Var[str] + + def _get_imports(self) -> imports.ImportDict: + merged_imports = super()._get_imports() + if self.tz is not None: + merged_imports = imports.merge_imports( + merged_imports, + {"moment-timezone": {ImportVar(tag="")}}, + ) + return merged_imports + + def get_event_triggers(self) -> Dict[str, Any]: + """Get the events triggers signatures for the component. + + Returns: + The signatures of the event triggers. + """ + return { + **super().get_event_triggers(), + "on_change": lambda date: [date], + } + + @classmethod + def create(cls, *children, **props) -> Component: + """Create a Moment component. + + Args: + *children: The children of the component. + **props: The properties of the component. + + Returns: + The Moment Component. + """ + comp = super().create(*children, **props) + if "tz" in props: + comp.lib_dependencies.append("moment-timezone") + return comp