reflex/reflex/components/layout/foreach.py
2023-11-03 16:20:42 -07:00

103 lines
3.3 KiB
Python

"""Create a list of components from an iterable."""
from __future__ import annotations
import typing
from typing import Any, Callable, Iterable
from reflex.components.component import Component
from reflex.components.layout.fragment import Fragment
from reflex.components.tags import IterTag
from reflex.vars import BaseVar, Var, get_unique_variable_name
class Foreach(Component):
"""A component that takes in an iterable and a render function and renders a list of components."""
# The iterable to create components from.
iterable: Var[Iterable]
# A function from the render args to the component.
render_fn: Callable = Fragment.create
@classmethod
def create(cls, iterable: Var[Iterable], render_fn: Callable, **props) -> Foreach:
"""Create a foreach component.
Args:
iterable: The iterable to create components from.
render_fn: A function from the render args to the component.
**props: The attributes to pass to each child component.
Returns:
The foreach component.
Raises:
TypeError: If the iterable is of type Any.
"""
try:
type_ = (
iterable._var_type
if iterable._var_type.mro()[0] == dict
else iterable._var_type.__args__[0]
)
except Exception:
type_ = Any
iterable = Var.create(iterable) # type: ignore
if iterable._var_type == Any:
raise TypeError(
f"Could not foreach over var of type Any. (If you are trying to foreach over a state var, add a type annotation to the var.)"
)
arg = BaseVar(_var_name="_", _var_type=type_, _var_is_local=True)
comp = IterTag(iterable=iterable, render_fn=render_fn).render_component(arg)
return cls(
iterable=iterable,
render_fn=render_fn,
children=[comp],
**props,
)
def _render(self) -> IterTag:
return IterTag(
iterable=self.iterable,
render_fn=self.render_fn,
index_var_name=get_unique_variable_name(),
)
def render(self):
"""Render the component.
Returns:
The dictionary for template of component.
"""
tag = self._render()
try:
type_ = (
tag.iterable._var_type
if tag.iterable._var_type.mro()[0] == dict
else typing.get_args(tag.iterable._var_type)[0]
)
except Exception:
type_ = Any
arg = BaseVar(
_var_name=get_unique_variable_name(),
_var_type=type_,
)
index_arg = tag.get_index_var_arg()
component = tag.render_component(arg)
return dict(
tag.add_props(
**self.event_triggers,
key=self.key,
sx=self.style,
id=self.id,
class_name=self.class_name,
).set(
children=[component.render()],
props=tag.format_props(),
),
iterable_state=tag.iterable._var_full_name,
arg_name=arg._var_name,
arg_index=index_arg,
iterable_type=tag.iterable._var_type.mro()[0].__name__,
)