diff --git a/pynecone/.templates/jinja/web/pages/index.js.jinja2 b/pynecone/.templates/jinja/web/pages/index.js.jinja2 index 00fef933e..2da298d25 100644 --- a/pynecone/.templates/jinja/web/pages/index.js.jinja2 +++ b/pynecone/.templates/jinja/web/pages/index.js.jinja2 @@ -19,10 +19,15 @@ export default function Component() { const { isReady } = {{const.router}} const { {{const.color_mode}}, {{const.toggle_color_mode}} } = {{const.use_color_mode}}() - const Event = events => {{state_name|react_setter}}({ - ...{{state_name}}, - events: [...{{state_name}}.events, ...events], - }) + const Event = (events, _e) => { + if (_e) { + _e.preventDefault(); + } + {{state_name|react_setter}}({ + ...{{state_name}}, + events: [...{{state_name}}.events, ...events], + }) + } const File = files => {{state_name|react_setter}}({ ...{{state_name}}, @@ -51,8 +56,8 @@ export default function Component() { } await updateState({{state_name}}, {{state_name|react_setter}}, {{const.result}}, {{const.result|react_setter}}, {{const.router}}, {{const.socket}}.current) - } - update() + } + update() }) useEffect(() => { const change_complete = () => Event([E('{{state_name}}.{{const.hydrate}}', {})]) diff --git a/pynecone/.templates/web/utils/state.js b/pynecone/.templates/web/utils/state.js index 946b6ee1b..51641b09c 100644 --- a/pynecone/.templates/web/utils/state.js +++ b/pynecone/.templates/web/utils/state.js @@ -11,6 +11,9 @@ let token; // Key for the token in the session storage. const TOKEN_KEY = "token"; +// Dictionary holding component references. +export const refs = {}; + /** * Generate a UUID (Used for session tokens). * Taken from: https://stackoverflow.com/questions/105034/how-do-i-create-a-guid-uuid @@ -93,8 +96,8 @@ export const applyEvent = async (event, router, socket) => { } if (event.name == "_set_value") { - event.payload.ref.current.blur(); - event.payload.ref.current.value = event.payload.value; + const ref = event.payload.ref in refs ? refs[event.payload.ref] : event.payload.ref; + ref.current.value = event.payload.value; return false; } diff --git a/pynecone/compiler/compiler.py b/pynecone/compiler/compiler.py index c449691f5..1a440ade7 100644 --- a/pynecone/compiler/compiler.py +++ b/pynecone/compiler/compiler.py @@ -26,6 +26,7 @@ DEFAULT_IMPORTS: imports.ImportDict = { ImportVar(tag="uploadFiles"), ImportVar(tag="E"), ImportVar(tag="isTrue"), + ImportVar(tag="refs"), }, "": {ImportVar(tag="focus-visible/dist/focus-visible")}, "@chakra-ui/react": {ImportVar(tag=constants.USE_COLOR_MODE)}, diff --git a/pynecone/components/component.py b/pynecone/components/component.py index 53c6ce635..86342df94 100644 --- a/pynecone/components/component.py +++ b/pynecone/components/component.py @@ -469,7 +469,7 @@ class Component(Base, ABC): """ ref = self.get_ref() if ref is not None: - return f"const {ref} = useRef(null);" + return f"const {ref} = useRef(null); refs['{ref}'] = {ref};" return None def get_hooks(self) -> Set[str]: diff --git a/pynecone/components/tags/tag.py b/pynecone/components/tags/tag.py index 20b9add54..5d4cbd1ec 100644 --- a/pynecone/components/tags/tag.py +++ b/pynecone/components/tags/tag.py @@ -64,6 +64,9 @@ class Tag(Base): Returns: The formatted prop to display within a tag. + + Raises: + TypeError: If the prop is not a valid type. """ # Handle var props. if isinstance(prop, Var): @@ -81,8 +84,8 @@ class Tag(Base): else: # All other events. chain = ",".join([format.format_event(event) for event in prop.events]) - event = f"{{{EVENT_ARG}.preventDefault(); Event([{chain}])}}" - prop = f"({EVENT_ARG}) => {event}" + event = f"Event([{chain}], {EVENT_ARG})" + prop = f"{EVENT_ARG} => {event}" # Handle other types. elif isinstance(prop, str): @@ -103,7 +106,12 @@ class Tag(Base): } # Dump the prop as JSON. - prop = format.json_dumps(prop) + try: + prop = format.json_dumps(prop) + except TypeError as e: + raise TypeError( + f"Could not format prop: {prop} of type {type(prop)}" + ) from e # This substitution is necessary to unwrap var values. prop = re.sub('"{', "", prop) diff --git a/pynecone/constants.py b/pynecone/constants.py index a80e1e85e..5fab3a8fb 100644 --- a/pynecone/constants.py +++ b/pynecone/constants.py @@ -8,19 +8,16 @@ from types import SimpleNamespace import pkg_resources # App names and versions. -# The name of the Pynecone module. +# The name of the Pynecone package. MODULE_NAME = "pynecone" -# The name of the pip install package. -PACKAGE_NAME = "pynecone" # The current version of Pynecone. -VERSION = pkg_resources.get_distribution(PACKAGE_NAME).version +VERSION = pkg_resources.get_distribution(MODULE_NAME).version # Minimum version of Node.js required to run Pynecone. MIN_NODE_VERSION = "16.6.0" # Valid bun versions. MIN_BUN_VERSION = "0.5.9" MAX_BUN_VERSION = "0.5.9" -INVALID_BUN_VERSIONS = ["0.5.5", "0.5.6", "0.5.7"] # Files and directories used to init a new project. # The root directory of the pynecone library. diff --git a/pynecone/utils/prerequisites.py b/pynecone/utils/prerequisites.py index 5e1865e73..56766271b 100644 --- a/pynecone/utils/prerequisites.py +++ b/pynecone/utils/prerequisites.py @@ -230,7 +230,6 @@ def validate_and_install_bun(initialize=True): if bun_version is not None and ( bun_version < version.parse(constants.MIN_BUN_VERSION) or bun_version > version.parse(constants.MAX_BUN_VERSION) - or str(bun_version) in constants.INVALID_BUN_VERSIONS ): console.print( f"""[red]Bun version {bun_version} is not supported by Pynecone. Please change your to bun version to be between {constants.MIN_BUN_VERSION} and {constants.MAX_BUN_VERSION}.""" diff --git a/tests/components/test_tag.py b/tests/components/test_tag.py index 2e53d0517..444c33a4b 100644 --- a/tests/components/test_tag.py +++ b/tests/components/test_tag.py @@ -25,7 +25,7 @@ def mock_event(arg): ({"a": 1, "b": 2, "c": 3}, '{{"a": 1, "b": 2, "c": 3}}'), ( EventChain(events=[EventSpec(handler=EventHandler(fn=mock_event))]), - '{(_e) => {_e.preventDefault(); Event([E("mock_event")])}}', + '{_e => Event([E("mock_event")], _e)}', ), ( EventChain( @@ -36,7 +36,7 @@ def mock_event(arg): ) ] ), - '{(_e) => {_e.preventDefault(); Event([E("mock_event", {arg:_e.target.value})])}}', + '{_e => Event([E("mock_event", {arg:_e.target.value})], _e)}', ), ({"a": "red", "b": "blue"}, '{{"a": "red", "b": "blue"}}'), (BaseVar(name="var", type_="int"), "{var}"),