Browse Source

Fix textarea rendering issue (#700)

PeterYusuke 2 năm trước cách đây
mục cha
commit
bd03e9290c

+ 9 - 1
pynecone/compiler/templates.py

@@ -154,7 +154,15 @@ UPLOAD_FN = path_ops.join(
         "}})",
     ]
 ).format
-
+FULL_CONTROL = path_ops.join(
+    [
+        "{{setState(prev => ({{",
+        "...prev,{state_name}: {arg}",
+        "}}), ",
+        "()=>Event([{chain}])",
+        ")}}",
+    ]
+).format
 
 # Effects.
 ROUTER = constants.ROUTER

+ 36 - 2
pynecone/components/component.py

@@ -134,7 +134,11 @@ class Component(Base, ABC):
 
             # Check if the key is an event trigger.
             if key in triggers:
-                kwargs["event_triggers"][key] = self._create_event_chain(key, value)
+                state_name = kwargs["value"].name if kwargs.get("value", False) else ""
+                full_control = self.is_full_control(kwargs)
+                kwargs["event_triggers"][key] = self._create_event_chain(
+                    key, value, state_name, full_control
+                )
 
         # Remove any keys that were added as events.
         for key in kwargs["event_triggers"]:
@@ -167,12 +171,16 @@ class Component(Base, ABC):
         value: Union[
             Var, EventHandler, EventSpec, List[Union[EventHandler, EventSpec]], Callable
         ],
+        state_name: str = "",
+        full_control: bool = False,
     ) -> Union[EventChain, Var]:
         """Create an event chain from a variety of input types.
 
         Args:
             event_trigger: The event trigger to bind the chain to.
             value: The value to create the event chain from.
+            state_name: The state to be fully controlled.
+            full_control: Whether full contorolled or not.
 
         Returns:
             The event chain.
@@ -240,8 +248,13 @@ class Component(Base, ABC):
                 for e in events
             ]
 
+        # set state name when fully controlled input
+        state_name = state_name if full_control else ""
+
         # Return the event chain.
-        return EventChain(events=events)
+        return EventChain(
+            events=events, state_name=state_name, full_control=full_control
+        )
 
     @classmethod
     def get_triggers(cls) -> Set[str]:
@@ -466,6 +479,27 @@ class Component(Base, ABC):
             custom_components |= child.get_custom_components(seen=seen)
         return custom_components
 
+    def is_full_control(self, kwargs: dict) -> bool:
+        """Return if the component is fully controlled input.
+
+        Args:
+            kwargs: The component kwargs.
+
+        Returns:
+            Whether fully controlled.
+        """
+        value = kwargs.get("value")
+        if value is None or type(value) != BaseVar:
+            return False
+
+        on_change = kwargs.get("on_change")
+        if on_change is None or type(on_change) != EventHandler:
+            return False
+
+        value = value.full_name
+        on_change = on_change.fn.__qualname__
+        return value == on_change.replace(constants.SETTER_PREFIX, "")
+
 
 # Map from component to styling.
 ComponentStyle = Dict[Union[str, Type[Component]], Any]

+ 3 - 0
pynecone/components/tags/tag.py

@@ -78,6 +78,9 @@ class Tag(Base):
             if len(prop.events) == 1 and prop.events[0].upload:
                 # Special case for upload events.
                 event = format.format_upload_event(prop.events[0])
+            elif prop.full_control:
+                # Full control component events.
+                event = format.format_full_control_event(prop)
             else:
                 # All other events.
                 chain = ",".join([format.format_event(event) for event in prop.events])

+ 6 - 0
pynecone/event.py

@@ -110,6 +110,12 @@ class EventChain(Base):
 
     events: List[EventSpec]
 
+    # Whether events are in fully controlled input.
+    full_control: bool = False
+
+    # State name when fully controlled.
+    state_name: str = ""
+
 
 class Target(Base):
     """A Javascript event target."""

+ 20 - 1
pynecone/utils/format.py

@@ -15,7 +15,7 @@ from pynecone import constants
 from pynecone.utils import types
 
 if TYPE_CHECKING:
-    from pynecone.event import EventHandler, EventSpec
+    from pynecone.event import EventChain, EventHandler, EventSpec
 
 WRAP_MAP = {
     "{": "}",
@@ -303,6 +303,25 @@ def format_upload_event(event_spec: EventSpec) -> str:
     return f'uploadFiles({parent_state}, {templates.RESULT}, {templates.SET_RESULT}, {parent_state}.files, "{state}.{name}",UPLOAD)'
 
 
+def format_full_control_event(event_chain: EventChain) -> str:
+    """Format a fully controlled input prop.
+
+    Args:
+        event_chain: The event chain for full controlled input.
+
+    Returns:
+        The compiled event.
+    """
+    from pynecone.compiler import templates
+
+    event_spec = event_chain.events[0]
+    arg = event_spec.args[0][1]
+    state_name = event_chain.state_name
+    chain = ",".join([format_event(event) for event in event_chain.events])
+    event = templates.FULL_CONTROL(state_name=state_name, arg=arg, chain=chain)
+    return event
+
+
 def format_query_params(router_data: Dict[str, Any]) -> Dict[str, str]:
     """Convert back query params name to python-friendly case.