Skip to content

Commit 4da32a1

Browse files
authored
allow dynamic icons name (#4636)
* allow dynamic icons name * handle literal vars * clean up code
1 parent 6e54652 commit 4da32a1

File tree

2 files changed

+88
-12
lines changed

2 files changed

+88
-12
lines changed

reflex/components/lucide/icon.py

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
from reflex.components.component import Component
44
from reflex.utils import format
5-
from reflex.vars.base import Var
5+
from reflex.utils.imports import ImportVar
6+
from reflex.vars.base import LiteralVar, Var
7+
from reflex.vars.sequence import LiteralStringVar
68

79

810
class LucideIconComponent(Component):
911
"""Lucide Icon Component."""
1012

11-
library = "lucide-react@0.469.0"
13+
library = "lucide-react@0.471.1"
1214

1315

1416
class Icon(LucideIconComponent):
@@ -32,39 +34,61 @@ def create(cls, *children, **props) -> Component:
3234
Raises:
3335
AttributeError: The errors tied to bad usage of the Icon component.
3436
ValueError: If the icon tag is invalid.
37+
TypeError: If the icon name is not a string.
3538
3639
Returns:
3740
The created component.
3841
"""
3942
if children:
4043
if len(children) == 1 and isinstance(children[0], str):
4144
props["tag"] = children[0]
42-
children = []
4345
else:
4446
raise AttributeError(
4547
f"Passing multiple children to Icon component is not allowed: remove positional arguments {children[1:]} to fix"
4648
)
4749
if "tag" not in props:
4850
raise AttributeError("Missing 'tag' keyword-argument for Icon")
4951

52+
tag: str | Var | LiteralVar = props.pop("tag")
53+
if isinstance(tag, LiteralVar):
54+
if isinstance(tag, LiteralStringVar):
55+
tag = tag._var_value
56+
else:
57+
raise TypeError(f"Icon name must be a string, got {type(tag)}")
58+
elif isinstance(tag, Var):
59+
return DynamicIcon.create(name=tag, **props)
60+
5061
if (
51-
not isinstance(props["tag"], str)
52-
or format.to_snake_case(props["tag"]) not in LUCIDE_ICON_LIST
62+
not isinstance(tag, str)
63+
or format.to_snake_case(tag) not in LUCIDE_ICON_LIST
5364
):
5465
raise ValueError(
55-
f"Invalid icon tag: {props['tag']}. Please use one of the following: {', '.join(LUCIDE_ICON_LIST[0:25])}, ..."
66+
f"Invalid icon tag: {tag}. Please use one of the following: {', '.join(LUCIDE_ICON_LIST[0:25])}, ..."
5667
"\nSee full list at https://lucide.dev/icons."
5768
)
5869

59-
if props["tag"] in LUCIDE_ICON_MAPPING_OVERRIDE:
60-
props["tag"] = LUCIDE_ICON_MAPPING_OVERRIDE[props["tag"]]
70+
if tag in LUCIDE_ICON_MAPPING_OVERRIDE:
71+
props["tag"] = LUCIDE_ICON_MAPPING_OVERRIDE[tag]
6172
else:
62-
props["tag"] = (
63-
format.to_title_case(format.to_snake_case(props["tag"])) + "Icon"
64-
)
73+
props["tag"] = format.to_title_case(format.to_snake_case(tag)) + "Icon"
6574
props["alias"] = f"Lucide{props['tag']}"
6675
props.setdefault("color", "var(--current-color)")
67-
return super().create(*children, **props)
76+
return super().create(**props)
77+
78+
79+
class DynamicIcon(LucideIconComponent):
80+
"""A DynamicIcon component."""
81+
82+
tag = "DynamicIcon"
83+
84+
name: Var[str]
85+
86+
def _get_imports(self):
87+
_imports = super()._get_imports()
88+
if self.library:
89+
_imports.pop(self.library)
90+
_imports["lucide-react/dynamic"] = [ImportVar("DynamicIcon", install=False)]
91+
return _imports
6892

6993

7094
LUCIDE_ICON_LIST = [
@@ -846,6 +870,7 @@ def create(cls, *children, **props) -> Component:
846870
"house",
847871
"house_plug",
848872
"house_plus",
873+
"house_wifi",
849874
"ice_cream_bowl",
850875
"ice_cream_cone",
851876
"id_card",
@@ -1534,6 +1559,7 @@ def create(cls, *children, **props) -> Component:
15341559
"trending_up_down",
15351560
"triangle",
15361561
"triangle_alert",
1562+
"triangle_dashed",
15371563
"triangle_right",
15381564
"trophy",
15391565
"truck",

reflex/components/lucide/icon.pyi

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,60 @@ class Icon(LucideIconComponent):
104104
Raises:
105105
AttributeError: The errors tied to bad usage of the Icon component.
106106
ValueError: If the icon tag is invalid.
107+
TypeError: If the icon name is not a string.
107108
108109
Returns:
109110
The created component.
110111
"""
111112
...
112113

114+
class DynamicIcon(LucideIconComponent):
115+
@overload
116+
@classmethod
117+
def create( # type: ignore
118+
cls,
119+
*children,
120+
name: Optional[Union[Var[str], str]] = None,
121+
style: Optional[Style] = None,
122+
key: Optional[Any] = None,
123+
id: Optional[Any] = None,
124+
class_name: Optional[Any] = None,
125+
autofocus: Optional[bool] = None,
126+
custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None,
127+
on_blur: Optional[EventType[[], BASE_STATE]] = None,
128+
on_click: Optional[EventType[[], BASE_STATE]] = None,
129+
on_context_menu: Optional[EventType[[], BASE_STATE]] = None,
130+
on_double_click: Optional[EventType[[], BASE_STATE]] = None,
131+
on_focus: Optional[EventType[[], BASE_STATE]] = None,
132+
on_mount: Optional[EventType[[], BASE_STATE]] = None,
133+
on_mouse_down: Optional[EventType[[], BASE_STATE]] = None,
134+
on_mouse_enter: Optional[EventType[[], BASE_STATE]] = None,
135+
on_mouse_leave: Optional[EventType[[], BASE_STATE]] = None,
136+
on_mouse_move: Optional[EventType[[], BASE_STATE]] = None,
137+
on_mouse_out: Optional[EventType[[], BASE_STATE]] = None,
138+
on_mouse_over: Optional[EventType[[], BASE_STATE]] = None,
139+
on_mouse_up: Optional[EventType[[], BASE_STATE]] = None,
140+
on_scroll: Optional[EventType[[], BASE_STATE]] = None,
141+
on_unmount: Optional[EventType[[], BASE_STATE]] = None,
142+
**props,
143+
) -> "DynamicIcon":
144+
"""Create the component.
145+
146+
Args:
147+
*children: The children of the component.
148+
style: The style of the component.
149+
key: A unique key for the component.
150+
id: The id for the component.
151+
class_name: The class name for the component.
152+
autofocus: Whether the component should take the focus once the page is loaded
153+
custom_attrs: custom attribute
154+
**props: The props of the component.
155+
156+
Returns:
157+
The component.
158+
"""
159+
...
160+
113161
LUCIDE_ICON_LIST = [
114162
"a_arrow_down",
115163
"a_arrow_up",
@@ -889,6 +937,7 @@ LUCIDE_ICON_LIST = [
889937
"house",
890938
"house_plug",
891939
"house_plus",
940+
"house_wifi",
892941
"ice_cream_bowl",
893942
"ice_cream_cone",
894943
"id_card",
@@ -1577,6 +1626,7 @@ LUCIDE_ICON_LIST = [
15771626
"trending_up_down",
15781627
"triangle",
15791628
"triangle_alert",
1629+
"triangle_dashed",
15801630
"triangle_right",
15811631
"trophy",
15821632
"truck",

0 commit comments

Comments
 (0)