Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow dynamic icons name #4636

Merged
merged 3 commits into from
Jan 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 38 additions & 12 deletions reflex/components/lucide/icon.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

from reflex.components.component import Component
from reflex.utils import format
from reflex.vars.base import Var
from reflex.utils.imports import ImportVar
from reflex.vars.base import LiteralVar, Var
from reflex.vars.sequence import LiteralStringVar


class LucideIconComponent(Component):
"""Lucide Icon Component."""

library = "lucide-react@0.469.0"
library = "lucide-react@0.471.1"


class Icon(LucideIconComponent):
Expand All @@ -32,39 +34,61 @@ def create(cls, *children, **props) -> Component:
Raises:
AttributeError: The errors tied to bad usage of the Icon component.
ValueError: If the icon tag is invalid.
TypeError: If the icon name is not a string.

Returns:
The created component.
"""
if children:
if len(children) == 1 and isinstance(children[0], str):
props["tag"] = children[0]
children = []
else:
raise AttributeError(
f"Passing multiple children to Icon component is not allowed: remove positional arguments {children[1:]} to fix"
)
if "tag" not in props:
raise AttributeError("Missing 'tag' keyword-argument for Icon")

tag: str | Var | LiteralVar = props.pop("tag")
if isinstance(tag, LiteralVar):
if isinstance(tag, LiteralStringVar):
tag = tag._var_value
else:
raise TypeError(f"Icon name must be a string, got {type(tag)}")
elif isinstance(tag, Var):
return DynamicIcon.create(name=tag, **props)

if (
not isinstance(props["tag"], str)
or format.to_snake_case(props["tag"]) not in LUCIDE_ICON_LIST
not isinstance(tag, str)
or format.to_snake_case(tag) not in LUCIDE_ICON_LIST
):
raise ValueError(
f"Invalid icon tag: {props['tag']}. Please use one of the following: {', '.join(LUCIDE_ICON_LIST[0:25])}, ..."
f"Invalid icon tag: {tag}. Please use one of the following: {', '.join(LUCIDE_ICON_LIST[0:25])}, ..."
"\nSee full list at https://lucide.dev/icons."
)

if props["tag"] in LUCIDE_ICON_MAPPING_OVERRIDE:
props["tag"] = LUCIDE_ICON_MAPPING_OVERRIDE[props["tag"]]
if tag in LUCIDE_ICON_MAPPING_OVERRIDE:
props["tag"] = LUCIDE_ICON_MAPPING_OVERRIDE[tag]
else:
props["tag"] = (
format.to_title_case(format.to_snake_case(props["tag"])) + "Icon"
)
props["tag"] = format.to_title_case(format.to_snake_case(tag)) + "Icon"
props["alias"] = f"Lucide{props['tag']}"
props.setdefault("color", "var(--current-color)")
return super().create(*children, **props)
return super().create(**props)


class DynamicIcon(LucideIconComponent):
"""A DynamicIcon component."""

tag = "DynamicIcon"

name: Var[str]

def _get_imports(self):
_imports = super()._get_imports()
if self.library:
_imports.pop(self.library)
_imports["lucide-react/dynamic"] = [ImportVar("DynamicIcon", install=False)]
return _imports
Lendemor marked this conversation as resolved.
Show resolved Hide resolved


LUCIDE_ICON_LIST = [
Expand Down Expand Up @@ -846,6 +870,7 @@ def create(cls, *children, **props) -> Component:
"house",
"house_plug",
"house_plus",
"house_wifi",
"ice_cream_bowl",
"ice_cream_cone",
"id_card",
Expand Down Expand Up @@ -1534,6 +1559,7 @@ def create(cls, *children, **props) -> Component:
"trending_up_down",
"triangle",
"triangle_alert",
"triangle_dashed",
"triangle_right",
"trophy",
"truck",
Expand Down
50 changes: 50 additions & 0 deletions reflex/components/lucide/icon.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,60 @@ class Icon(LucideIconComponent):
Raises:
AttributeError: The errors tied to bad usage of the Icon component.
ValueError: If the icon tag is invalid.
TypeError: If the icon name is not a string.

Returns:
The created component.
"""
...

class DynamicIcon(LucideIconComponent):
@overload
@classmethod
def create( # type: ignore
cls,
*children,
name: Optional[Union[Var[str], str]] = None,
style: Optional[Style] = None,
key: Optional[Any] = None,
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None,
on_blur: Optional[EventType[[], BASE_STATE]] = None,
on_click: Optional[EventType[[], BASE_STATE]] = None,
on_context_menu: Optional[EventType[[], BASE_STATE]] = None,
on_double_click: Optional[EventType[[], BASE_STATE]] = None,
on_focus: Optional[EventType[[], BASE_STATE]] = None,
on_mount: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_down: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_enter: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_leave: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_move: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_out: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_over: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_up: Optional[EventType[[], BASE_STATE]] = None,
on_scroll: Optional[EventType[[], BASE_STATE]] = None,
on_unmount: Optional[EventType[[], BASE_STATE]] = None,
**props,
) -> "DynamicIcon":
"""Create the component.

Args:
*children: The children of the component.
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.

Returns:
The component.
"""
...

LUCIDE_ICON_LIST = [
"a_arrow_down",
"a_arrow_up",
Expand Down Expand Up @@ -889,6 +937,7 @@ LUCIDE_ICON_LIST = [
"house",
"house_plug",
"house_plus",
"house_wifi",
"ice_cream_bowl",
"ice_cream_cone",
"id_card",
Expand Down Expand Up @@ -1577,6 +1626,7 @@ LUCIDE_ICON_LIST = [
"trending_up_down",
"triangle",
"triangle_alert",
"triangle_dashed",
"triangle_right",
"trophy",
"truck",
Expand Down
Loading