Explorar o código

bring helmet to fix issues with head in component, bonus fix for next/script to make reflex web compile

Khaleel Al-Adhami hai 2 meses
pai
achega
5d42c71fa4

+ 6 - 6
reflex/compiler/utils.py

@@ -17,6 +17,7 @@ from reflex.components.base import Description, Image, Scripts
 from reflex.components.base.document import Links, ScrollRestoration
 from reflex.components.base.document import Meta as ReactMeta
 from reflex.components.component import Component, ComponentStyle, CustomComponent
+from reflex.components.core.helmet import Helmet
 from reflex.components.el.elements.metadata import Head, Meta, Title
 from reflex.components.el.elements.other import Html
 from reflex.components.el.elements.sectioning import Body
@@ -494,13 +495,12 @@ def add_meta(
         children.append(Description.create(content=description))
     children.append(Image.create(content=image))
 
-    if False:
-        page.children.append(
-            Head.create(
-                *children,
-                *meta_tags,
-            )
+    page.children.append(
+        Helmet.create(
+            *children,
+            *meta_tags,
         )
+    )
 
     return page
 

+ 56 - 60
reflex/components/base/script.py

@@ -1,75 +1,71 @@
-"""Next.js script wrappers and inline script functionality.
-
-https://nextjs.org/docs/app/api-reference/components/script
-"""
+"""Wrapper for the script element. Uses the Helmet component to manage the head."""
 
 from __future__ import annotations
 
-from typing import Literal
-
-from reflex.components.component import Component
-from reflex.event import EventHandler, no_args_event_spec
-from reflex.vars.base import LiteralVar, Var
-
-
-class Script(Component):
-    """Next.js script component.
-
-    Note that this component differs from reflex.components.base.document.NextScript
-    in that it is intended for use with custom and user-defined scripts.
-
-    It also differs from reflex.components.base.link.ScriptTag, which is the plain
-    HTML <script> tag which does not work when rendering a component.
-    """
-
-    library = "next/script"
-    tag = "Script"
-    is_default = True
+from reflex.components import el as elements
+from reflex.components.core.helmet import helmet
+from reflex.utils import console
 
-    # Required unless inline script is used
-    src: Var[str]
 
-    # When the script will execute: afterInteractive (defer-like behavior) | beforeInteractive | lazyOnload (async-like behavior)
-    strategy: Var[Literal["afterInteractive", "beforeInteractive", "lazyOnload"]] = (
-        LiteralVar.create("afterInteractive")
-    )
-
-    # Triggered when the script is loading
-    on_load: EventHandler[no_args_event_spec]
-
-    # Triggered when the script has loaded
-    on_ready: EventHandler[no_args_event_spec]
-
-    # Triggered when the script has errored
-    on_error: EventHandler[no_args_event_spec]
+class Script(elements.Script):
+    """Wrapper for the script element."""
 
     @classmethod
-    def create(cls, *children, **props) -> Component:
-        """Create an inline or user-defined script.
-
-        If a string is provided as the first child, it will be rendered as an inline script
-        otherwise the `src` prop must be provided.
-
-        The following event triggers are provided:
-
-        on_load: Execute code after the script has finished loading.
-        on_ready: Execute code after the script has finished loading and every
-            time the component is mounted.
-        on_error: Execute code if the script fails to load.
+    def create(
+        cls,
+        *children,
+        **props,
+    ):
+        """Display the script element.
 
         Args:
-            *children: The children of the component.
-            **props: The props of the component.
+            *children: The children of the element.
+            **props: The properties of the element.
 
         Returns:
-            The component.
-
-        Raises:
-            ValueError: when neither children nor `src` are specified.
+            The script element.
         """
-        if not children and not props.get("src"):
-            raise ValueError("Must provide inline script or `src` prop.")
-        return super().create(*children, **props)
+        async_ = props.pop("async_", None)
+        char_set = props.pop("char_set", None)
+        cross_origin = props.pop("cross_origin", None)
+        defer = props.pop("defer", None)
+        integrity = props.pop("integrity", None)
+        referrer_policy = props.pop("referrer_policy", None)
+        src = props.pop("src", None)
+        type = props.pop("type", None)
+        key = props.pop("key", None)
+        id = props.pop("id", None)
+        class_name = props.pop("class_name", None)
+        autofocus = props.pop("autofocus", None)
+        custom_attrs = props.pop("custom_attrs", None)
+        on_mount = props.pop("on_mount", None)
+        on_unmount = props.pop("on_unmount", None)
+
+        if props:
+            console.warn(
+                f"rx.script does not support the following properties: {list(props.keys())}"
+            )
+
+        return helmet(
+            elements.Script.create(
+                *children,
+                async_=async_,
+                char_set=char_set,
+                cross_origin=cross_origin,
+                defer=defer,
+                integrity=integrity,
+                referrer_policy=referrer_policy,
+                src=src,
+                type=type,
+                key=key,
+                id=id,
+                class_name=class_name,
+                autofocus=autofocus,
+                custom_attrs=custom_attrs,
+                on_mount=on_mount,
+                on_unmount=on_unmount,
+            )
+        )
 
 
 script = Script.create

+ 240 - 29
reflex/components/base/script.pyi

@@ -5,21 +5,229 @@
 # ------------------------------------------------------
 from typing import Any, Literal, Optional, overload
 
-from reflex.components.component import Component
+from reflex.components import el as elements
 from reflex.event import EventType
 from reflex.style import Style
 from reflex.vars.base import Var
 
-class Script(Component):
+class Script(elements.Script):
     @overload
     @classmethod
     def create(  # type: ignore
         cls,
         *children,
+        async_: Var[bool] | bool | None = None,
+        char_set: Var[str] | str | None = None,
+        cross_origin: Literal["", "anonymous", "use-credentials"]
+        | Var[Literal["", "anonymous", "use-credentials"]]
+        | None = None,
+        defer: Var[bool] | bool | None = None,
+        integrity: Var[str] | str | None = None,
+        referrer_policy: Literal[
+            "",
+            "no-referrer",
+            "no-referrer-when-downgrade",
+            "origin",
+            "origin-when-cross-origin",
+            "same-origin",
+            "strict-origin",
+            "strict-origin-when-cross-origin",
+            "unsafe-url",
+        ]
+        | Var[
+            Literal[
+                "",
+                "no-referrer",
+                "no-referrer-when-downgrade",
+                "origin",
+                "origin-when-cross-origin",
+                "same-origin",
+                "strict-origin",
+                "strict-origin-when-cross-origin",
+                "unsafe-url",
+            ]
+        ]
+        | None = None,
         src: Var[str] | str | None = None,
-        strategy: Literal["afterInteractive", "beforeInteractive", "lazyOnload"]
-        | Var[Literal["afterInteractive", "beforeInteractive", "lazyOnload"]]
+        type: Var[str] | str | None = None,
+        access_key: Var[str] | str | None = None,
+        auto_capitalize: Literal[
+            "characters", "none", "off", "on", "sentences", "words"
+        ]
+        | Var[Literal["characters", "none", "off", "on", "sentences", "words"]]
+        | None = None,
+        content_editable: Literal["inherit", "plaintext-only", False, True]
+        | Var[Literal["inherit", "plaintext-only", False, True]]
+        | None = None,
+        context_menu: Var[str] | str | None = None,
+        dir: Var[str] | str | None = None,
+        draggable: Var[bool] | bool | None = None,
+        enter_key_hint: Literal[
+            "done", "enter", "go", "next", "previous", "search", "send"
+        ]
+        | Var[Literal["done", "enter", "go", "next", "previous", "search", "send"]]
+        | None = None,
+        hidden: Var[bool] | bool | None = None,
+        input_mode: Literal[
+            "decimal", "email", "none", "numeric", "search", "tel", "text", "url"
+        ]
+        | Var[
+            Literal[
+                "decimal", "email", "none", "numeric", "search", "tel", "text", "url"
+            ]
+        ]
         | None = None,
+        item_prop: Var[str] | str | None = None,
+        lang: Var[str] | str | None = None,
+        role: Literal[
+            "alert",
+            "alertdialog",
+            "application",
+            "article",
+            "banner",
+            "button",
+            "cell",
+            "checkbox",
+            "columnheader",
+            "combobox",
+            "complementary",
+            "contentinfo",
+            "definition",
+            "dialog",
+            "directory",
+            "document",
+            "feed",
+            "figure",
+            "form",
+            "grid",
+            "gridcell",
+            "group",
+            "heading",
+            "img",
+            "link",
+            "list",
+            "listbox",
+            "listitem",
+            "log",
+            "main",
+            "marquee",
+            "math",
+            "menu",
+            "menubar",
+            "menuitem",
+            "menuitemcheckbox",
+            "menuitemradio",
+            "navigation",
+            "none",
+            "note",
+            "option",
+            "presentation",
+            "progressbar",
+            "radio",
+            "radiogroup",
+            "region",
+            "row",
+            "rowgroup",
+            "rowheader",
+            "scrollbar",
+            "search",
+            "searchbox",
+            "separator",
+            "slider",
+            "spinbutton",
+            "status",
+            "switch",
+            "tab",
+            "table",
+            "tablist",
+            "tabpanel",
+            "term",
+            "textbox",
+            "timer",
+            "toolbar",
+            "tooltip",
+            "tree",
+            "treegrid",
+            "treeitem",
+        ]
+        | Var[
+            Literal[
+                "alert",
+                "alertdialog",
+                "application",
+                "article",
+                "banner",
+                "button",
+                "cell",
+                "checkbox",
+                "columnheader",
+                "combobox",
+                "complementary",
+                "contentinfo",
+                "definition",
+                "dialog",
+                "directory",
+                "document",
+                "feed",
+                "figure",
+                "form",
+                "grid",
+                "gridcell",
+                "group",
+                "heading",
+                "img",
+                "link",
+                "list",
+                "listbox",
+                "listitem",
+                "log",
+                "main",
+                "marquee",
+                "math",
+                "menu",
+                "menubar",
+                "menuitem",
+                "menuitemcheckbox",
+                "menuitemradio",
+                "navigation",
+                "none",
+                "note",
+                "option",
+                "presentation",
+                "progressbar",
+                "radio",
+                "radiogroup",
+                "region",
+                "row",
+                "rowgroup",
+                "rowheader",
+                "scrollbar",
+                "search",
+                "searchbox",
+                "separator",
+                "slider",
+                "spinbutton",
+                "status",
+                "switch",
+                "tab",
+                "table",
+                "tablist",
+                "tabpanel",
+                "term",
+                "textbox",
+                "timer",
+                "toolbar",
+                "tooltip",
+                "tree",
+                "treegrid",
+                "treeitem",
+            ]
+        ]
+        | None = None,
+        slot: Var[str] | str | None = None,
+        spell_check: Var[bool] | bool | None = None,
+        tab_index: Var[int] | int | None = None,
+        title: Var[str] | str | None = None,
         style: Style | None = None,
         key: Any | None = None,
         id: Any | None = None,
@@ -30,9 +238,7 @@ class Script(Component):
         on_click: Optional[EventType[()]] = None,
         on_context_menu: Optional[EventType[()]] = None,
         on_double_click: Optional[EventType[()]] = None,
-        on_error: Optional[EventType[()]] = None,
         on_focus: Optional[EventType[()]] = None,
-        on_load: Optional[EventType[()]] = None,
         on_mount: Optional[EventType[()]] = None,
         on_mouse_down: Optional[EventType[()]] = None,
         on_mouse_enter: Optional[EventType[()]] = None,
@@ -41,43 +247,48 @@ class Script(Component):
         on_mouse_out: Optional[EventType[()]] = None,
         on_mouse_over: Optional[EventType[()]] = None,
         on_mouse_up: Optional[EventType[()]] = None,
-        on_ready: Optional[EventType[()]] = None,
         on_scroll: Optional[EventType[()]] = None,
         on_unmount: Optional[EventType[()]] = None,
         **props,
     ) -> "Script":
-        """Create an inline or user-defined script.
-
-        If a string is provided as the first child, it will be rendered as an inline script
-        otherwise the `src` prop must be provided.
-
-        The following event triggers are provided:
-
-        on_load: Execute code after the script has finished loading.
-        on_ready: Execute code after the script has finished loading and every
-            time the component is mounted.
-        on_error: Execute code if the script fails to load.
+        """Display the script element.
 
         Args:
-            *children: The children of the component.
-            src: Required unless inline script is used
-            strategy: When the script will execute: afterInteractive (defer-like behavior) | beforeInteractive | lazyOnload (async-like behavior)
-            on_load: Triggered when the script is loading
-            on_ready: Triggered when the script has loaded
-            on_error: Triggered when the script has errored
+            *children: The children of the element.
+            async_: Indicates that the script should be executed asynchronously
+            char_set: Character encoding of the external script
+            cross_origin: Configures the CORS requests for the script
+            defer: Indicates that the script should be executed after the page has finished parsing
+            integrity: Security feature allowing browsers to verify what they fetch
+            referrer_policy: Specifies which referrer information to send when fetching the script
+            src: URL of an external script
+            type: Specifies the MIME type of the script
+            access_key: Provides a hint for generating a keyboard shortcut for the current element.
+            auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
+            content_editable: Indicates whether the element's content is editable.
+            context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
+            dir: Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left)
+            draggable: Defines whether the element can be dragged.
+            enter_key_hint: Hints what media types the media element is able to play.
+            hidden: Defines whether the element is hidden.
+            input_mode: Defines the type of the element.
+            item_prop: Defines the name of the element for metadata purposes.
+            lang: Defines the language used in the element.
+            role: Defines the role of the element.
+            slot: Assigns a slot in a shadow DOM shadow tree to an element.
+            spell_check: Defines whether the element may be checked for spelling errors.
+            tab_index: Defines the position of the current element in the tabbing order.
+            title: Defines a tooltip for the element.
             style: The style of the component.
             key: A unique key for the component.
             id: The id for the component.
             class_name: The class name for the component.
             autofocus: Whether the component should take the focus once the page is loaded
             custom_attrs: custom attribute
-            **props: The props of the component.
+            **props: The properties of the element.
 
         Returns:
-            The component.
-
-        Raises:
-            ValueError: when neither children nor `src` are specified.
+            The script element.
         """
         ...
 

+ 1 - 0
reflex/components/core/__init__.py

@@ -28,6 +28,7 @@ _SUBMOD_ATTRS: dict[str, list[str]] = {
         "Foreach",
     ],
     "html": ["html", "Html"],
+    "helmet": ["Helmet"],
     "match": [
         "match",
         "Match",

+ 1 - 0
reflex/components/core/__init__.pyi

@@ -25,6 +25,7 @@ from .debounce import DebounceInput as DebounceInput
 from .debounce import debounce_input as debounce_input
 from .foreach import Foreach as Foreach
 from .foreach import foreach as foreach
+from .helmet import Helmet as Helmet
 from .html import Html as Html
 from .html import html as html
 from .match import Match as Match

+ 14 - 0
reflex/components/core/helmet.py

@@ -0,0 +1,14 @@
+"""Helmet component module."""
+
+from reflex.components.component import Component
+
+
+class Helmet(Component):
+    """A helmet component."""
+
+    library = "react-helmet@6.1.0"
+
+    tag = "Helmet"
+
+
+helmet = Helmet.create

+ 1 - 0
reflex/constants/installer.py

@@ -86,6 +86,7 @@ class PackageJson(SimpleNamespace):
         "json5": "2.2.3",
         "react-router": "7.3.0",
         "react-router-dom": "7.3.0",
+        "react-helmet": "6.1.0",
         "@react-router/node": "7.3.0",
         "@react-router/serve": "7.3.0",
         "react": "19.0.0",