component as literal vars
This commit is contained in:
parent
3eab2b6e7d
commit
42b8586ab9
@ -36,14 +36,10 @@
|
|||||||
{# component: component dictionary #}
|
{# component: component dictionary #}
|
||||||
{% macro render_tag(component) %}
|
{% macro render_tag(component) %}
|
||||||
<{{component.name}} {{- render_props(component.props) }}>
|
<{{component.name}} {{- render_props(component.props) }}>
|
||||||
{%- if component.args is not none -%}
|
{{ component.contents }}
|
||||||
{{- render_arg_content(component) }}
|
{% for child in component.children %}
|
||||||
{%- else -%}
|
{{ render(child) }}
|
||||||
{{ component.contents }}
|
{% endfor %}
|
||||||
{% for child in component.children %}
|
|
||||||
{{ render(child) }}
|
|
||||||
{% endfor %}
|
|
||||||
{%- endif -%}
|
|
||||||
</{{component.name}}>
|
</{{component.name}}>
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ from typing import Any, Iterator
|
|||||||
from reflex.components.component import Component
|
from reflex.components.component import Component
|
||||||
from reflex.components.tags import Tag
|
from reflex.components.tags import Tag
|
||||||
from reflex.components.tags.tagless import Tagless
|
from reflex.components.tags.tagless import Tagless
|
||||||
from reflex.vars import ArrayVar, BooleanVar, ObjectVar, Var
|
from reflex.vars import BooleanVar, ObjectVar, Var
|
||||||
|
|
||||||
|
|
||||||
class Bare(Component):
|
class Bare(Component):
|
||||||
@ -33,7 +33,7 @@ class Bare(Component):
|
|||||||
|
|
||||||
def _render(self) -> Tag:
|
def _render(self) -> Tag:
|
||||||
if isinstance(self.contents, Var):
|
if isinstance(self.contents, Var):
|
||||||
if isinstance(self.contents, (BooleanVar, ObjectVar, ArrayVar)):
|
if isinstance(self.contents, (BooleanVar, ObjectVar)):
|
||||||
return Tagless(contents=f"{{{str(self.contents.to_string())}}}")
|
return Tagless(contents=f"{{{str(self.contents.to_string())}}}")
|
||||||
return Tagless(contents=f"{{{str(self.contents)}}}")
|
return Tagless(contents=f"{{{str(self.contents)}}}")
|
||||||
return Tagless(contents=str(self.contents))
|
return Tagless(contents=str(self.contents))
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
import dataclasses
|
||||||
import typing
|
import typing
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from functools import lru_cache, wraps
|
from functools import lru_cache, wraps
|
||||||
@ -58,7 +59,14 @@ from reflex.utils.imports import (
|
|||||||
parse_imports,
|
parse_imports,
|
||||||
)
|
)
|
||||||
from reflex.vars import VarData
|
from reflex.vars import VarData
|
||||||
from reflex.vars.base import LiteralVar, Var
|
from reflex.vars.base import (
|
||||||
|
CachedVarOperation,
|
||||||
|
LiteralVar,
|
||||||
|
Var,
|
||||||
|
cached_property_no_lock,
|
||||||
|
)
|
||||||
|
from reflex.vars.function import FunctionStringVar
|
||||||
|
from reflex.vars.object import ObjectVar
|
||||||
from reflex.vars.sequence import LiteralArrayVar
|
from reflex.vars.sequence import LiteralArrayVar
|
||||||
|
|
||||||
|
|
||||||
@ -2340,3 +2348,119 @@ class MemoizationLeaf(Component):
|
|||||||
|
|
||||||
|
|
||||||
load_dynamic_serializer()
|
load_dynamic_serializer()
|
||||||
|
|
||||||
|
|
||||||
|
class ComponentVar(Var[Component], python_types=Component):
|
||||||
|
"""A Var that represents a Component."""
|
||||||
|
|
||||||
|
|
||||||
|
def empty_component() -> Component:
|
||||||
|
"""Create an empty component.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
An empty component.
|
||||||
|
"""
|
||||||
|
from reflex.components.base.bare import Bare
|
||||||
|
|
||||||
|
return Bare.create("")
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass(
|
||||||
|
eq=False,
|
||||||
|
frozen=True,
|
||||||
|
)
|
||||||
|
class LiteralComponentVar(CachedVarOperation, LiteralVar, ComponentVar):
|
||||||
|
"""A Var that represents a Component."""
|
||||||
|
|
||||||
|
_var_value: Component = dataclasses.field(default_factory=empty_component)
|
||||||
|
|
||||||
|
@cached_property_no_lock
|
||||||
|
def _cached_var_name(self) -> str:
|
||||||
|
"""Get the name of the var.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The name of the var.
|
||||||
|
"""
|
||||||
|
tag = self._var_value._render()
|
||||||
|
|
||||||
|
props = Var.create(tag.props).to(ObjectVar)
|
||||||
|
for prop in tag.special_props:
|
||||||
|
props = props.merge(prop)
|
||||||
|
|
||||||
|
contents = getattr(self._var_value, "contents", None)
|
||||||
|
|
||||||
|
tag_name = Var(tag.name) if tag.name else Var("Fragment")
|
||||||
|
|
||||||
|
return str(
|
||||||
|
FunctionStringVar.create(
|
||||||
|
"jsx",
|
||||||
|
).call(
|
||||||
|
tag_name,
|
||||||
|
props,
|
||||||
|
*([Var.create(contents)] if contents is not None else []),
|
||||||
|
*[Var.create(child) for child in self._var_value.children],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@cached_property_no_lock
|
||||||
|
def _cached_get_all_var_data(self) -> VarData | None:
|
||||||
|
"""Get the VarData for the var.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The VarData for the var.
|
||||||
|
"""
|
||||||
|
return VarData.merge(
|
||||||
|
VarData(
|
||||||
|
imports={
|
||||||
|
"@emotion/react": [
|
||||||
|
ImportVar(tag="jsx"),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
),
|
||||||
|
VarData(
|
||||||
|
imports=self._var_value._get_all_imports(collapse=True),
|
||||||
|
),
|
||||||
|
*(
|
||||||
|
[
|
||||||
|
VarData(
|
||||||
|
imports={
|
||||||
|
"react": [
|
||||||
|
ImportVar(tag="Fragment"),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
if not self._var_value.tag
|
||||||
|
else []
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
"""Get the hash of the var.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The hash of the var.
|
||||||
|
"""
|
||||||
|
return hash((self.__class__.__name__,))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(
|
||||||
|
cls,
|
||||||
|
value: Component,
|
||||||
|
_var_data: VarData | None = None,
|
||||||
|
):
|
||||||
|
"""Create a var from a value.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: The value of the var.
|
||||||
|
_var_data: Additional hooks and imports associated with the Var.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The var.
|
||||||
|
"""
|
||||||
|
return LiteralComponentVar(
|
||||||
|
_js_expr="",
|
||||||
|
_var_type=type(value),
|
||||||
|
_var_data=_var_data,
|
||||||
|
_var_value=value,
|
||||||
|
)
|
||||||
|
@ -5,11 +5,17 @@ from __future__ import annotations
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Callable, ClassVar, Dict, List, Optional, Tuple
|
from typing import Any, Callable, ClassVar, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
from reflex.components.component import Component, ComponentNamespace, MemoizationLeaf
|
from reflex.components.component import (
|
||||||
|
Component,
|
||||||
|
ComponentNamespace,
|
||||||
|
MemoizationLeaf,
|
||||||
|
StatefulComponent,
|
||||||
|
)
|
||||||
from reflex.components.el.elements.forms import Input
|
from reflex.components.el.elements.forms import Input
|
||||||
from reflex.components.radix.themes.layout.box import Box
|
from reflex.components.radix.themes.layout.box import Box
|
||||||
from reflex.config import environment
|
from reflex.config import environment
|
||||||
from reflex.constants import Dirs
|
from reflex.constants import Dirs
|
||||||
|
from reflex.constants.compiler import Imports
|
||||||
from reflex.event import (
|
from reflex.event import (
|
||||||
CallableEventSpec,
|
CallableEventSpec,
|
||||||
EventChain,
|
EventChain,
|
||||||
@ -19,9 +25,10 @@ from reflex.event import (
|
|||||||
call_script,
|
call_script,
|
||||||
parse_args_spec,
|
parse_args_spec,
|
||||||
)
|
)
|
||||||
|
from reflex.utils import format
|
||||||
from reflex.utils.imports import ImportVar
|
from reflex.utils.imports import ImportVar
|
||||||
from reflex.vars import VarData
|
from reflex.vars import VarData
|
||||||
from reflex.vars.base import CallableVar, LiteralVar, Var
|
from reflex.vars.base import CallableVar, LiteralVar, Var, get_unique_variable_name
|
||||||
from reflex.vars.sequence import LiteralStringVar
|
from reflex.vars.sequence import LiteralStringVar
|
||||||
|
|
||||||
DEFAULT_UPLOAD_ID: str = "default"
|
DEFAULT_UPLOAD_ID: str = "default"
|
||||||
@ -179,9 +186,7 @@ class Upload(MemoizationLeaf):
|
|||||||
|
|
||||||
library = "react-dropzone@14.2.10"
|
library = "react-dropzone@14.2.10"
|
||||||
|
|
||||||
tag = "ReactDropzone"
|
tag = ""
|
||||||
|
|
||||||
is_default = True
|
|
||||||
|
|
||||||
# The list of accepted file types. This should be a dictionary of MIME types as keys and array of file formats as
|
# The list of accepted file types. This should be a dictionary of MIME types as keys and array of file formats as
|
||||||
# values.
|
# values.
|
||||||
@ -201,7 +206,7 @@ class Upload(MemoizationLeaf):
|
|||||||
min_size: Var[int]
|
min_size: Var[int]
|
||||||
|
|
||||||
# Whether to allow multiple files to be uploaded.
|
# Whether to allow multiple files to be uploaded.
|
||||||
multiple: Var[bool] = True # type: ignore
|
multiple: Var[bool]
|
||||||
|
|
||||||
# Whether to disable click to upload.
|
# Whether to disable click to upload.
|
||||||
no_click: Var[bool]
|
no_click: Var[bool]
|
||||||
@ -219,11 +224,12 @@ class Upload(MemoizationLeaf):
|
|||||||
on_drop: EventHandler[_on_drop_spec]
|
on_drop: EventHandler[_on_drop_spec]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, *children, **props) -> Component:
|
def create(cls, *children, multiple=True, **props) -> Component:
|
||||||
"""Create an upload component.
|
"""Create an upload component.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
*children: The children of the component.
|
*children: The children of the component.
|
||||||
|
multiple: Whether to allow multiple files to be uploaded.
|
||||||
**props: The properties of the component.
|
**props: The properties of the component.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -232,6 +238,8 @@ class Upload(MemoizationLeaf):
|
|||||||
# Mark the Upload component as used in the app.
|
# Mark the Upload component as used in the app.
|
||||||
cls.is_used = True
|
cls.is_used = True
|
||||||
|
|
||||||
|
props["multiple"] = multiple
|
||||||
|
|
||||||
# Apply the default classname
|
# Apply the default classname
|
||||||
given_class_name = props.pop("class_name", [])
|
given_class_name = props.pop("class_name", [])
|
||||||
if isinstance(given_class_name, str):
|
if isinstance(given_class_name, str):
|
||||||
@ -243,17 +251,6 @@ class Upload(MemoizationLeaf):
|
|||||||
upload_props = {
|
upload_props = {
|
||||||
key: value for key, value in props.items() if key in supported_props
|
key: value for key, value in props.items() if key in supported_props
|
||||||
}
|
}
|
||||||
# The file input to use.
|
|
||||||
upload = Input.create(type="file")
|
|
||||||
upload.special_props = [Var(_js_expr="{...getInputProps()}", _var_type=None)]
|
|
||||||
|
|
||||||
# The dropzone to use.
|
|
||||||
zone = Box.create(
|
|
||||||
upload,
|
|
||||||
*children,
|
|
||||||
**{k: v for k, v in props.items() if k not in supported_props},
|
|
||||||
)
|
|
||||||
zone.special_props = [Var(_js_expr="{...getRootProps()}", _var_type=None)]
|
|
||||||
|
|
||||||
# Create the component.
|
# Create the component.
|
||||||
upload_props["id"] = props.get("id", DEFAULT_UPLOAD_ID)
|
upload_props["id"] = props.get("id", DEFAULT_UPLOAD_ID)
|
||||||
@ -275,9 +272,71 @@ class Upload(MemoizationLeaf):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
upload_props["on_drop"] = on_drop
|
upload_props["on_drop"] = on_drop
|
||||||
|
|
||||||
|
input_props_unique_name = get_unique_variable_name()
|
||||||
|
root_props_unique_name = get_unique_variable_name()
|
||||||
|
|
||||||
|
event_var, callback_str = StatefulComponent._get_memoized_event_triggers(
|
||||||
|
Box.create(on_click=upload_props["on_drop"])
|
||||||
|
)["on_click"]
|
||||||
|
|
||||||
|
upload_props["on_drop"] = event_var
|
||||||
|
|
||||||
|
upload_props = {
|
||||||
|
format.to_camel_case(key): value for key, value in upload_props.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
var_data = VarData.merge(
|
||||||
|
VarData(
|
||||||
|
imports=Imports.EVENTS,
|
||||||
|
hooks={
|
||||||
|
"const [addEvents, connectError] = useContext(EventLoopContext);": None
|
||||||
|
},
|
||||||
|
),
|
||||||
|
event_var._get_all_var_data(),
|
||||||
|
VarData(
|
||||||
|
hooks={
|
||||||
|
callback_str: None,
|
||||||
|
f"const {{getRootProps: {root_props_unique_name}, getInputProps: {input_props_unique_name}}} = useDropzone({
|
||||||
|
str(Var.create({
|
||||||
|
'onDrop': event_var,
|
||||||
|
**upload_props,
|
||||||
|
}))
|
||||||
|
});": None,
|
||||||
|
},
|
||||||
|
imports={
|
||||||
|
"react-dropzone": "useDropzone",
|
||||||
|
**Imports.EVENTS,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# The file input to use.
|
||||||
|
upload = Input.create(type="file")
|
||||||
|
upload.special_props = [
|
||||||
|
Var(
|
||||||
|
_js_expr=f"{{...{input_props_unique_name}()}}",
|
||||||
|
_var_type=None,
|
||||||
|
_var_data=var_data,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
# The dropzone to use.
|
||||||
|
zone = Box.create(
|
||||||
|
upload,
|
||||||
|
*children,
|
||||||
|
**{k: v for k, v in props.items() if k not in supported_props},
|
||||||
|
)
|
||||||
|
zone.special_props = [
|
||||||
|
Var(
|
||||||
|
_js_expr=f"{{...{root_props_unique_name}()}}",
|
||||||
|
_var_type=None,
|
||||||
|
_var_data=var_data,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
return super().create(
|
return super().create(
|
||||||
zone,
|
zone,
|
||||||
**upload_props,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -295,11 +354,6 @@ class Upload(MemoizationLeaf):
|
|||||||
return (arg_value[0], placeholder)
|
return (arg_value[0], placeholder)
|
||||||
return arg_value
|
return arg_value
|
||||||
|
|
||||||
def _render(self):
|
|
||||||
out = super()._render()
|
|
||||||
out.args = ("getRootProps", "getInputProps")
|
|
||||||
return out
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_app_wrap_components() -> dict[tuple[int, str], Component]:
|
def _get_app_wrap_components() -> dict[tuple[int, str], Component]:
|
||||||
return {
|
return {
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import dataclasses
|
import dataclasses
|
||||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
from typing import Any, Dict, List, Optional, Union
|
||||||
|
|
||||||
from reflex.event import EventChain
|
from reflex.event import EventChain
|
||||||
from reflex.utils import format, types
|
from reflex.utils import format, types
|
||||||
@ -23,9 +23,6 @@ class Tag:
|
|||||||
# The inner contents of the tag.
|
# The inner contents of the tag.
|
||||||
contents: str = ""
|
contents: str = ""
|
||||||
|
|
||||||
# Args to pass to the tag.
|
|
||||||
args: Optional[Tuple[str, ...]] = None
|
|
||||||
|
|
||||||
# Special props that aren't key value pairs.
|
# Special props that aren't key value pairs.
|
||||||
special_props: List[Var] = dataclasses.field(default_factory=list)
|
special_props: List[Var] = dataclasses.field(default_factory=list)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user