Переглянути джерело

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

Khaleel Al-Adhami 2 місяців тому
батько
коміт
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 Links, ScrollRestoration
 from reflex.components.base.document import Meta as ReactMeta
 from reflex.components.base.document import Meta as ReactMeta
 from reflex.components.component import Component, ComponentStyle, CustomComponent
 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.metadata import Head, Meta, Title
 from reflex.components.el.elements.other import Html
 from reflex.components.el.elements.other import Html
 from reflex.components.el.elements.sectioning import Body
 from reflex.components.el.elements.sectioning import Body
@@ -494,13 +495,12 @@ def add_meta(
         children.append(Description.create(content=description))
         children.append(Description.create(content=description))
     children.append(Image.create(content=image))
     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
     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 __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
     @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:
         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:
         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
 script = Script.create

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

@@ -5,21 +5,229 @@
 # ------------------------------------------------------
 # ------------------------------------------------------
 from typing import Any, Literal, Optional, overload
 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.event import EventType
 from reflex.style import Style
 from reflex.style import Style
 from reflex.vars.base import Var
 from reflex.vars.base import Var
 
 
-class Script(Component):
+class Script(elements.Script):
     @overload
     @overload
     @classmethod
     @classmethod
     def create(  # type: ignore
     def create(  # type: ignore
         cls,
         cls,
         *children,
         *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,
         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,
         | 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,
         style: Style | None = None,
         key: Any | None = None,
         key: Any | None = None,
         id: Any | None = None,
         id: Any | None = None,
@@ -30,9 +238,7 @@ class Script(Component):
         on_click: Optional[EventType[()]] = None,
         on_click: Optional[EventType[()]] = None,
         on_context_menu: Optional[EventType[()]] = None,
         on_context_menu: Optional[EventType[()]] = None,
         on_double_click: Optional[EventType[()]] = None,
         on_double_click: Optional[EventType[()]] = None,
-        on_error: Optional[EventType[()]] = None,
         on_focus: Optional[EventType[()]] = None,
         on_focus: Optional[EventType[()]] = None,
-        on_load: Optional[EventType[()]] = None,
         on_mount: Optional[EventType[()]] = None,
         on_mount: Optional[EventType[()]] = None,
         on_mouse_down: Optional[EventType[()]] = None,
         on_mouse_down: Optional[EventType[()]] = None,
         on_mouse_enter: Optional[EventType[()]] = None,
         on_mouse_enter: Optional[EventType[()]] = None,
@@ -41,43 +247,48 @@ class Script(Component):
         on_mouse_out: Optional[EventType[()]] = None,
         on_mouse_out: Optional[EventType[()]] = None,
         on_mouse_over: Optional[EventType[()]] = None,
         on_mouse_over: Optional[EventType[()]] = None,
         on_mouse_up: Optional[EventType[()]] = None,
         on_mouse_up: Optional[EventType[()]] = None,
-        on_ready: Optional[EventType[()]] = None,
         on_scroll: Optional[EventType[()]] = None,
         on_scroll: Optional[EventType[()]] = None,
         on_unmount: Optional[EventType[()]] = None,
         on_unmount: Optional[EventType[()]] = None,
         **props,
         **props,
     ) -> "Script":
     ) -> "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:
         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.
             style: The style of the component.
             key: A unique key for the component.
             key: A unique key for the component.
             id: The id for the component.
             id: The id for the component.
             class_name: The class name for the component.
             class_name: The class name for the component.
             autofocus: Whether the component should take the focus once the page is loaded
             autofocus: Whether the component should take the focus once the page is loaded
             custom_attrs: custom attribute
             custom_attrs: custom attribute
-            **props: The props of the component.
+            **props: The properties of the element.
 
 
         Returns:
         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",
         "Foreach",
     ],
     ],
     "html": ["html", "Html"],
     "html": ["html", "Html"],
+    "helmet": ["Helmet"],
     "match": [
     "match": [
         "match",
         "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 .debounce import debounce_input as debounce_input
 from .foreach import Foreach as Foreach
 from .foreach import Foreach as Foreach
 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 .html import html as html
 from .html import html as html
 from .match import Match as Match
 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",
         "json5": "2.2.3",
         "react-router": "7.3.0",
         "react-router": "7.3.0",
         "react-router-dom": "7.3.0",
         "react-router-dom": "7.3.0",
+        "react-helmet": "6.1.0",
         "@react-router/node": "7.3.0",
         "@react-router/node": "7.3.0",
         "@react-router/serve": "7.3.0",
         "@react-router/serve": "7.3.0",
         "react": "19.0.0",
         "react": "19.0.0",