reflex 0.4.9a1__py3-none-any.whl → 0.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of reflex might be problematic. Click here for more details.

Files changed (133) hide show
  1. reflex/.templates/apps/blank/code/blank.py +19 -16
  2. reflex/.templates/apps/demo/code/demo.py +1 -1
  3. reflex/.templates/apps/demo/code/pages/datatable.py +4 -4
  4. reflex/.templates/apps/demo/code/pages/forms.py +2 -2
  5. reflex/.templates/jinja/web/tailwind.config.js.jinja2 +12 -0
  6. reflex/.templates/web/utils/helpers/debounce.js +17 -0
  7. reflex/.templates/web/utils/helpers/throttle.js +22 -0
  8. reflex/.templates/web/utils/state.js +21 -3
  9. reflex/__init__.py +6 -1
  10. reflex/__init__.pyi +4 -1
  11. reflex/app.py +160 -140
  12. reflex/app_module_for_backend.py +1 -1
  13. reflex/base.py +13 -15
  14. reflex/compiler/compiler.py +10 -1
  15. reflex/compiler/utils.py +3 -30
  16. reflex/components/__init__.py +1 -0
  17. reflex/components/chakra/datadisplay/list.py +1 -3
  18. reflex/components/chakra/datadisplay/list.pyi +3 -3
  19. reflex/components/chakra/disclosure/accordion.py +1 -1
  20. reflex/components/chakra/forms/pininput.pyi +1 -1
  21. reflex/components/chakra/media/icon.py +2 -2
  22. reflex/components/component.py +275 -32
  23. reflex/components/core/__init__.py +2 -2
  24. reflex/components/core/cond.py +1 -10
  25. reflex/components/core/debounce.py +5 -2
  26. reflex/components/core/debounce.pyi +4 -2
  27. reflex/components/core/foreach.py +60 -49
  28. reflex/components/core/html.py +6 -0
  29. reflex/components/core/match.py +2 -17
  30. reflex/components/core/upload.py +42 -1
  31. reflex/components/core/upload.pyi +199 -1
  32. reflex/components/datadisplay/code.py +7 -3
  33. reflex/components/datadisplay/code.pyi +3 -1
  34. reflex/components/el/elements/forms.py +1 -1
  35. reflex/components/el/elements/forms.pyi +1 -1
  36. reflex/components/lucide/icon.py +5 -13
  37. reflex/components/lucide/icon.pyi +0 -1
  38. reflex/components/markdown/markdown.py +5 -23
  39. reflex/components/markdown/markdown.pyi +1 -4
  40. reflex/components/radix/primitives/accordion.py +265 -410
  41. reflex/components/radix/primitives/accordion.pyi +390 -36
  42. reflex/components/radix/primitives/form.py +33 -29
  43. reflex/components/radix/primitives/form.pyi +7 -2
  44. reflex/components/radix/primitives/progress.py +17 -9
  45. reflex/components/radix/primitives/progress.pyi +2 -0
  46. reflex/components/radix/primitives/slider.py +30 -18
  47. reflex/components/radix/primitives/slider.pyi +4 -0
  48. reflex/components/radix/themes/base.py +8 -1
  49. reflex/components/radix/themes/base.pyi +79 -1
  50. reflex/components/radix/themes/color_mode.py +88 -20
  51. reflex/components/radix/themes/color_mode.pyi +157 -139
  52. reflex/components/radix/themes/components/__init__.py +17 -0
  53. reflex/components/radix/themes/components/badge.py +2 -1
  54. reflex/components/radix/themes/components/badge.pyi +3 -1
  55. reflex/components/radix/themes/components/button.py +3 -1
  56. reflex/components/radix/themes/components/button.pyi +4 -1
  57. reflex/components/radix/themes/components/checkbox_cards.py +48 -0
  58. reflex/components/radix/themes/components/checkbox_cards.pyi +264 -0
  59. reflex/components/radix/themes/components/checkbox_group.py +42 -0
  60. reflex/components/radix/themes/components/checkbox_group.pyi +253 -0
  61. reflex/components/radix/themes/components/data_list.py +63 -0
  62. reflex/components/radix/themes/components/data_list.pyi +426 -0
  63. reflex/components/radix/themes/components/icon_button.py +20 -17
  64. reflex/components/radix/themes/components/icon_button.pyi +5 -1
  65. reflex/components/radix/themes/components/progress.py +55 -0
  66. reflex/components/radix/themes/components/progress.pyi +180 -0
  67. reflex/components/radix/themes/components/radio.py +31 -0
  68. reflex/components/radix/themes/components/radio.pyi +169 -0
  69. reflex/components/radix/themes/components/radio_cards.py +48 -0
  70. reflex/components/radix/themes/components/radio_cards.pyi +264 -0
  71. reflex/components/radix/themes/components/radio_group.py +2 -4
  72. reflex/components/radix/themes/components/segmented_control.py +48 -0
  73. reflex/components/radix/themes/components/segmented_control.pyi +262 -0
  74. reflex/components/radix/themes/components/skeleton.py +32 -0
  75. reflex/components/radix/themes/components/skeleton.pyi +106 -0
  76. reflex/components/radix/themes/components/spinner.py +26 -0
  77. reflex/components/radix/themes/components/spinner.pyi +101 -0
  78. reflex/components/radix/themes/components/tabs.py +26 -1
  79. reflex/components/radix/themes/components/tabs.pyi +69 -9
  80. reflex/components/radix/themes/components/text_field.py +101 -71
  81. reflex/components/radix/themes/components/text_field.pyi +81 -499
  82. reflex/components/radix/themes/layout/base.py +2 -2
  83. reflex/components/radix/themes/layout/base.pyi +4 -4
  84. reflex/components/radix/themes/layout/center.py +8 -3
  85. reflex/components/radix/themes/layout/center.pyi +2 -1
  86. reflex/components/radix/themes/layout/container.py +30 -2
  87. reflex/components/radix/themes/layout/container.pyi +9 -30
  88. reflex/components/radix/themes/layout/list.py +10 -5
  89. reflex/components/radix/themes/layout/list.pyi +5 -21
  90. reflex/components/radix/themes/layout/spacer.py +8 -3
  91. reflex/components/radix/themes/layout/spacer.pyi +2 -1
  92. reflex/components/radix/themes/layout/stack.py +7 -1
  93. reflex/components/radix/themes/layout/stack.pyi +3 -3
  94. reflex/components/radix/themes/typography/link.py +10 -2
  95. reflex/components/radix/themes/typography/link.pyi +5 -4
  96. reflex/components/sonner/__init__.py +3 -0
  97. reflex/components/sonner/toast.py +267 -0
  98. reflex/components/sonner/toast.pyi +205 -0
  99. reflex/components/tags/iter_tag.py +9 -6
  100. reflex/config.py +30 -54
  101. reflex/constants/__init__.py +0 -2
  102. reflex/constants/base.py +0 -5
  103. reflex/constants/colors.py +2 -0
  104. reflex/constants/installer.py +6 -1
  105. reflex/constants/route.py +4 -0
  106. reflex/custom_components/custom_components.py +24 -2
  107. reflex/event.py +75 -30
  108. reflex/experimental/__init__.py +5 -0
  109. reflex/experimental/layout.py +24 -6
  110. reflex/model.py +2 -1
  111. reflex/page.py +7 -4
  112. reflex/reflex.py +8 -3
  113. reflex/route.py +39 -0
  114. reflex/state.py +128 -131
  115. reflex/style.py +25 -3
  116. reflex/testing.py +10 -6
  117. reflex/utils/console.py +3 -1
  118. reflex/utils/exec.py +20 -7
  119. reflex/utils/format.py +1 -1
  120. reflex/utils/imports.py +3 -1
  121. reflex/utils/prerequisites.py +141 -20
  122. reflex/utils/processes.py +21 -1
  123. reflex/utils/pyi_generator.py +100 -5
  124. reflex/utils/serializers.py +1 -1
  125. reflex/utils/telemetry.py +26 -4
  126. reflex/utils/types.py +62 -18
  127. reflex/vars.py +11 -5
  128. {reflex-0.4.9a1.dist-info → reflex-0.5.0.dist-info}/METADATA +16 -4
  129. {reflex-0.4.9a1.dist-info → reflex-0.5.0.dist-info}/RECORD +132 -110
  130. {reflex-0.4.9a1.dist-info → reflex-0.5.0.dist-info}/WHEEL +1 -1
  131. reflex/app.pyi +0 -149
  132. {reflex-0.4.9a1.dist-info → reflex-0.5.0.dist-info}/LICENSE +0 -0
  133. {reflex-0.4.9a1.dist-info → reflex-0.5.0.dist-info}/entry_points.txt +0 -0
@@ -5,302 +5,69 @@ from __future__ import annotations
5
5
  from typing import Any, Dict, List, Literal, Optional, Union
6
6
 
7
7
  from reflex.components.component import Component, ComponentNamespace
8
- from reflex.components.core.match import Match
8
+ from reflex.components.core.colors import color
9
+ from reflex.components.core.cond import cond
9
10
  from reflex.components.lucide.icon import Icon
10
11
  from reflex.components.radix.primitives.base import RadixPrimitiveComponent
11
- from reflex.components.radix.themes.base import LiteralAccentColor
12
- from reflex.style import (
13
- Style,
14
- convert_dict_to_style_and_format_emotion,
15
- format_as_emotion,
16
- )
12
+ from reflex.components.radix.themes.base import LiteralAccentColor, LiteralRadius
13
+ from reflex.style import Style
17
14
  from reflex.utils import imports
18
- from reflex.vars import BaseVar, Var, VarData, get_uuid_string_var
15
+ from reflex.vars import Var, get_uuid_string_var
19
16
 
20
17
  LiteralAccordionType = Literal["single", "multiple"]
21
18
  LiteralAccordionDir = Literal["ltr", "rtl"]
22
19
  LiteralAccordionOrientation = Literal["vertical", "horizontal"]
23
- LiteralAccordionRootVariant = Literal["classic", "soft", "surface", "outline", "ghost"]
24
- LiteralAccordionRootColorScheme = Literal["primary", "accent"]
20
+ LiteralAccordionVariant = Literal["classic", "soft", "surface", "outline", "ghost"]
25
21
 
26
22
  DEFAULT_ANIMATION_DURATION = 250
23
+ DEFAULT_ANIMATION_EASING = "cubic-bezier(0.87, 0, 0.13, 1)"
27
24
 
28
25
 
29
- def get_theme_accordion_root(variant: Var[str], color_scheme: Var[str]) -> BaseVar:
30
- """Get the theme for the accordion root component.
26
+ def _inherited_variant_selector(
27
+ variant: Var[LiteralAccordionVariant] | LiteralAccordionVariant,
28
+ *selectors: str,
29
+ ) -> str:
30
+ """Create a multi CSS selector for targeting variant against the given selectors.
31
31
 
32
32
  Args:
33
- variant: The variant of the accordion.
34
- color_scheme: The color of the accordion.
33
+ variant: The variant to target.
34
+ selectors: The selectors to apply the variant to (default &)
35
35
 
36
36
  Returns:
37
- The theme for the accordion root component.
37
+ A CSS selector that is more specific on elements that directly set the variant.
38
38
  """
39
- return Match.create( # type: ignore
40
- variant,
41
- (
42
- "soft",
43
- convert_dict_to_style_and_format_emotion(
44
- {
45
- "border_radius": "6px",
46
- "background_color": f"var(--{color_scheme}-3)",
47
- "box_shadow": "0 2px 10px var(--black-a1)",
48
- }
49
- ),
50
- ),
51
- (
52
- "outline",
53
- convert_dict_to_style_and_format_emotion(
54
- {
55
- "border_radius": "6px",
56
- "border": f"1px solid var(--{color_scheme}-6)",
57
- "box_shadow": "0 2px 10px var(--black-a1)",
58
- }
59
- ),
60
- ),
61
- (
62
- "surface",
63
- convert_dict_to_style_and_format_emotion(
64
- {
65
- "border_radius": "6px",
66
- "border": f"1px solid var(--{color_scheme}-6)",
67
- "background_color": f"var(--{color_scheme}-3)",
68
- "box_shadow": "0 2px 10px var(--black-a1)",
69
- }
70
- ),
71
- ),
72
- (
73
- "ghost",
74
- convert_dict_to_style_and_format_emotion(
75
- {
76
- "border_radius": "6px",
77
- "background_color": "none",
78
- "box_shadow": "None",
79
- }
80
- ),
81
- ),
82
- convert_dict_to_style_and_format_emotion(
83
- {
84
- "border_radius": "6px",
85
- "background_color": f"var(--{color_scheme}-9)",
86
- "box_shadow": "0 2px 10px var(--black-a4)",
87
- }
88
- ),
89
- # defaults to classic
90
- )
91
-
92
-
93
- def get_theme_accordion_item():
94
- """Get the theme for the accordion item component.
95
-
96
- Returns:
97
- The theme for the accordion item component.
98
- """
99
- return convert_dict_to_style_and_format_emotion(
100
- {
101
- "overflow": "hidden",
102
- "width": "100%",
103
- "margin_top": "1px",
104
- "&:first-child": {
105
- "margin_top": 0,
106
- "border_top_left_radius": "4px",
107
- "border_top_right_radius": "4px",
108
- },
109
- "&:last-child": {
110
- "border_bottom_left_radius": "4px",
111
- "border_bottom_right_radius": "4px",
112
- },
113
- "&:focus-within": {
114
- "position": "relative",
115
- "z_index": 1,
116
- },
117
- }
39
+ if not selectors:
40
+ selectors = ("&",)
41
+ # Prefer the `data-variant` that is set directly on the selector,
42
+ # but also inherit the `data-variant` from any parent element.
43
+ return ", ".join(
44
+ [
45
+ f"{selector}[data-variant='{variant}'], *:where([data-variant='{variant}']) {selector}"
46
+ for selector in selectors
47
+ ]
118
48
  )
119
49
 
120
50
 
121
- def get_theme_accordion_header() -> dict[str, str]:
122
- """Get the theme for the accordion header component.
123
-
124
- Returns:
125
- The theme for the accordion header component.
126
- """
127
- return {
128
- "display": "flex",
129
- }
130
-
131
-
132
- def get_theme_accordion_trigger(variant: str | Var, color_scheme: str | Var) -> BaseVar:
133
- """Get the theme for the accordion trigger component.
134
-
135
- Args:
136
- variant: The variant of the accordion.
137
- color_scheme: The color of the accordion.
51
+ class AccordionComponent(RadixPrimitiveComponent):
52
+ """Base class for all @radix-ui/accordion components."""
138
53
 
139
- Returns:
140
- The theme for the accordion trigger component.
141
- """
142
- return Match.create( # type: ignore
143
- variant,
144
- (
145
- "soft",
146
- convert_dict_to_style_and_format_emotion(
147
- {
148
- "color": f"var(--{color_scheme}-11)",
149
- "&:hover": {
150
- "background_color": f"var(--{color_scheme}-4)",
151
- },
152
- "& > .AccordionChevron": {
153
- "color": f"var(--{color_scheme}-11)",
154
- "transition": f"transform {DEFAULT_ANIMATION_DURATION}ms cubic-bezier(0.87, 0, 0.13, 1)",
155
- },
156
- "&[data-state='open'] > .AccordionChevron": {
157
- "transform": "rotate(180deg)",
158
- },
159
- "font_family": "inherit",
160
- "width": "100%",
161
- "padding": "0 20px",
162
- "height": "45px",
163
- "flex": 1,
164
- "display": "flex",
165
- "align_items": "center",
166
- "justify_content": "space-between",
167
- "font_size": "15px",
168
- "line_height": 1,
169
- }
170
- ),
171
- ),
172
- (
173
- "outline",
174
- "surface",
175
- "ghost",
176
- convert_dict_to_style_and_format_emotion(
177
- {
178
- "color": f"var(--{color_scheme}-11)",
179
- "&:hover": {
180
- "background_color": f"var(--{color_scheme}-4)",
181
- },
182
- "& > .AccordionChevron": {
183
- "color": f"var(--{color_scheme}-11)",
184
- "transition": f"transform {DEFAULT_ANIMATION_DURATION}ms cubic-bezier(0.87, 0, 0.13, 1)",
185
- },
186
- "&[data-state='open'] > .AccordionChevron": {
187
- "transform": "rotate(180deg)",
188
- },
189
- "font_family": "inherit",
190
- "width": "100%",
191
- "padding": "0 20px",
192
- "height": "45px",
193
- "flex": 1,
194
- "display": "flex",
195
- "align_items": "center",
196
- "justify_content": "space-between",
197
- "font_size": "15px",
198
- "line_height": 1,
199
- }
200
- ),
201
- ),
202
- # defaults to classic
203
- convert_dict_to_style_and_format_emotion(
204
- {
205
- "color": f"var(--{color_scheme}-9-contrast)",
206
- "box_shadow": f"var(--{color_scheme}-11)",
207
- "&:hover": {
208
- "background_color": f"var(--{color_scheme}-10)",
209
- },
210
- "& > .AccordionChevron": {
211
- "color": f"var(--{color_scheme}-9-contrast)",
212
- "transition": f"transform {DEFAULT_ANIMATION_DURATION}ms cubic-bezier(0.87, 0, 0.13, 1)",
213
- },
214
- "&[data-state='open'] > .AccordionChevron": {
215
- "transform": "rotate(180deg)",
216
- },
217
- "font_family": "inherit",
218
- "width": "100%",
219
- "padding": "0 20px",
220
- "height": "45px",
221
- "flex": 1,
222
- "display": "flex",
223
- "align_items": "center",
224
- "justify_content": "space-between",
225
- "font_size": "15px",
226
- "line_height": 1,
227
- }
228
- ),
229
- )
54
+ library = "@radix-ui/react-accordion@^1.1.2"
230
55
 
56
+ # The color scheme of the component.
57
+ color_scheme: Var[LiteralAccentColor]
231
58
 
232
- def get_theme_accordion_content(variant: str | Var, color_scheme: str | Var) -> BaseVar:
233
- """Get the theme for the accordion content component.
59
+ # The variant of the component.
60
+ variant: Var[LiteralAccordionVariant]
234
61
 
235
- Args:
236
- variant: The variant of the accordion.
237
- color_scheme: The color of the accordion.
62
+ def add_style(self) -> Style | None:
63
+ """Add style to the component."""
64
+ if self.color_scheme is not None:
65
+ self.custom_attrs["data-accent-color"] = self.color_scheme
66
+ if self.variant is not None:
67
+ self.custom_attrs["data-variant"] = self.variant
238
68
 
239
- Returns:
240
- The theme for the accordion content component.
241
- """
242
- return Match.create( # type: ignore
243
- variant,
244
- (
245
- "outline",
246
- "ghost",
247
- convert_dict_to_style_and_format_emotion(
248
- {
249
- "overflow": "hidden",
250
- "font_size": "10px",
251
- "color": f"var(--{color_scheme}-11)",
252
- "padding": "15px 20px",
253
- "&[data-state='open']": {
254
- "animation": Var.create(
255
- f"${{slideDown}} {DEFAULT_ANIMATION_DURATION}ms cubic-bezier(0.87, 0, 0.13, 1)",
256
- _var_is_string=True,
257
- ),
258
- },
259
- "&[data-state='closed']": {
260
- "animation": Var.create(
261
- f"${{slideUp}} {DEFAULT_ANIMATION_DURATION}ms cubic-bezier(0.87, 0, 0.13, 1)",
262
- _var_is_string=True,
263
- ),
264
- },
265
- }
266
- ),
267
- ),
268
- convert_dict_to_style_and_format_emotion(
269
- {
270
- "overflow": "hidden",
271
- "font_size": "10px",
272
- "color": Match.create(
273
- variant,
274
- ("classic", f"var(--{color_scheme}-9-contrast)"),
275
- f"var(--{color_scheme}-11)",
276
- ),
277
- "background_color": Match.create(
278
- variant,
279
- ("classic", f"var(--{color_scheme}-9)"),
280
- f"var(--{color_scheme}-3)",
281
- ),
282
- "padding": "15px 20px",
283
- "&[data-state='open']": {
284
- "animation": Var.create(
285
- f"${{slideDown}} {DEFAULT_ANIMATION_DURATION}ms cubic-bezier(0.87, 0, 0.13, 1)",
286
- _var_is_string=True,
287
- ),
288
- },
289
- "&[data-state='closed']": {
290
- "animation": Var.create(
291
- f"${{slideUp}} {DEFAULT_ANIMATION_DURATION}ms cubic-bezier(0.87, 0, 0.13, 1)",
292
- _var_is_string=True,
293
- ),
294
- },
295
- }
296
- ),
297
- )
298
-
299
-
300
- class AccordionComponent(RadixPrimitiveComponent):
301
- """Base class for all @radix-ui/accordion components."""
302
-
303
- library = "@radix-ui/react-accordion@^1.1.2"
69
+ def _exclude_props(self) -> list[str]:
70
+ return ["color_scheme", "variant"]
304
71
 
305
72
 
306
73
  class AccordionRoot(AccordionComponent):
@@ -314,10 +81,10 @@ class AccordionRoot(AccordionComponent):
314
81
  type: Var[LiteralAccordionType]
315
82
 
316
83
  # The value of the item to expand.
317
- value: Var[Optional[Union[str, List[str]]]]
84
+ value: Var[Union[str, List[str]]]
318
85
 
319
86
  # The default value of the item to expand.
320
- default_value: Var[Optional[Union[str, List[str]]]]
87
+ default_value: Var[Union[str, List[str]]]
321
88
 
322
89
  # Whether or not the accordion is collapsible.
323
90
  collapsible: Var[bool]
@@ -331,106 +98,27 @@ class AccordionRoot(AccordionComponent):
331
98
  # The orientation of the accordion.
332
99
  orientation: Var[LiteralAccordionOrientation]
333
100
 
334
- # The variant of the accordion.
335
- variant: Var[LiteralAccordionRootVariant] = "classic" # type: ignore
101
+ # The radius of the accordion corners.
102
+ radius: Var[LiteralRadius]
336
103
 
337
- # The color scheme of the accordion.
338
- color_scheme: Var[LiteralAccentColor] # type: ignore
104
+ # The time in milliseconds to animate open and close
105
+ duration: Var[int] = Var.create_safe(DEFAULT_ANIMATION_DURATION)
339
106
 
340
- # dynamic themes of the accordion generated at compile time.
341
- _dynamic_themes: Var[dict] = Var.create({}) # type: ignore
107
+ # The easing function to use for the animation.
108
+ easing: Var[str] = Var.create_safe(DEFAULT_ANIMATION_EASING)
342
109
 
343
- # The var_data associated with the component.
344
- _var_data: VarData = VarData() # type: ignore
110
+ # Whether to show divider lines between items.
111
+ show_dividers: Var[bool]
345
112
 
346
113
  _valid_children: List[str] = ["AccordionItem"]
347
114
 
348
- @classmethod
349
- def create(cls, *children, **props) -> Component:
350
- """Create the Accordion root component.
351
-
352
- Args:
353
- *children: The children of the component.
354
- **props: The properties of the component.
355
-
356
- Returns:
357
- The Accordion root Component.
358
- """
359
- comp = super().create(*children, **props)
360
-
361
- if comp.color_scheme is not None and not comp.color_scheme._var_state: # type: ignore
362
- # mark the vars of color string literals as strings so they can be formatted properly when performing a var operation.
363
- comp.color_scheme._var_is_string = True # type: ignore
364
-
365
- if comp.variant is not None and not comp.variant._var_state: # type: ignore
366
- # mark the vars of variant string literals as strings so they are formatted properly in the match condition.
367
- comp.variant._var_is_string = True # type: ignore
368
-
369
- return comp
370
-
371
- def _get_style(self) -> dict:
372
- """Get the style for the component.
373
-
374
- Returns:
375
- The dictionary of the component style as value and the style notation as key.
376
- """
377
- return {"css": self._dynamic_themes._merge(format_as_emotion(self.style))} # type: ignore
378
-
379
- def _apply_theme(self, theme: Component):
380
- global_color_scheme = getattr(theme, "accent_color", None)
381
-
382
- if global_color_scheme is None and self.color_scheme is None:
383
- raise ValueError(
384
- "`color_scheme` cannot be None. Either set the `color_scheme` prop on the accordion "
385
- "component or set the `accent_color` prop in your global theme."
386
- )
387
-
388
- # prepare the color_scheme var to be used in an f-string(strip off the wrapping curly brace)
389
- color_scheme = Var.create(
390
- self.color_scheme if self.color_scheme is not None else global_color_scheme
391
- )._replace( # type: ignore
392
- _var_is_string=False
393
- )
394
-
395
- accordion_theme_root = get_theme_accordion_root(
396
- variant=self.variant, color_scheme=color_scheme
397
- )
398
- accordion_theme_content = get_theme_accordion_content(
399
- variant=self.variant, color_scheme=color_scheme
400
- )
401
- accordion_theme_trigger = get_theme_accordion_trigger(
402
- variant=self.variant, color_scheme=color_scheme
403
- )
404
-
405
- # extract var_data from dynamic themes.
406
- self._var_data = (
407
- self._var_data.merge( # type: ignore
408
- accordion_theme_trigger._var_data,
409
- accordion_theme_content._var_data,
410
- accordion_theme_root._var_data,
411
- )
412
- or self._var_data
413
- )
414
-
415
- self._dynamic_themes = Var.create( # type: ignore
416
- convert_dict_to_style_and_format_emotion(
417
- {
418
- "& .AccordionItem": get_theme_accordion_item(),
419
- "& .AccordionHeader": get_theme_accordion_header(),
420
- "& .AccordionTrigger": accordion_theme_trigger,
421
- "& .AccordionContent": accordion_theme_content,
422
- }
423
- )
424
- )._merge( # type: ignore
425
- accordion_theme_root
426
- )
427
-
428
- def _get_imports(self):
429
- return imports.merge_imports(
430
- super()._get_imports(),
431
- self._var_data.imports if self._var_data else {},
432
- {"@emotion/react": [imports.ImportVar(tag="keyframes")]},
433
- )
115
+ def _exclude_props(self) -> list[str]:
116
+ return super()._exclude_props() + [
117
+ "radius",
118
+ "duration",
119
+ "easing",
120
+ "show_dividers",
121
+ ]
434
122
 
435
123
  def get_event_triggers(self) -> Dict[str, Any]:
436
124
  """Get the events triggers signatures for the component.
@@ -443,28 +131,48 @@ class AccordionRoot(AccordionComponent):
443
131
  "on_value_change": lambda e0: [e0],
444
132
  }
445
133
 
446
- def _get_custom_code(self) -> str:
447
- return """
448
- const slideDown = keyframes`
449
- from {
450
- height: 0;
451
- }
452
- to {
453
- height: var(--radix-accordion-content-height);
454
- }
455
- `
456
- const slideUp = keyframes`
457
- from {
458
- height: var(--radix-accordion-content-height);
459
- }
460
- to {
461
- height: 0;
462
- }
463
- `
464
- """
134
+ def add_style(self):
135
+ """Add style to the component.
465
136
 
466
- def _exclude_props(self) -> list[str]:
467
- return ["color_scheme", "variant"]
137
+ Returns:
138
+ The style of the component.
139
+ """
140
+ if self.radius is not None:
141
+ self.custom_attrs["data-radius"] = self.radius
142
+ if self.variant is None:
143
+ # The default variant is classic
144
+ self.custom_attrs["data-variant"] = "classic"
145
+
146
+ style = {
147
+ "border_radius": "var(--radius-4)",
148
+ "box_shadow": f"0 2px 10px {color('black', 1, alpha=True)}",
149
+ "&[data-variant='classic']": {
150
+ "background_color": color("accent", 9),
151
+ "box_shadow": f"0 2px 10px {color('black', 4, alpha=True)}",
152
+ },
153
+ "&[data-variant='soft']": {
154
+ "background_color": color("accent", 3),
155
+ },
156
+ "&[data-variant='outline']": {
157
+ "border": f"1px solid {color('accent', 6)}",
158
+ },
159
+ "&[data-variant='surface']": {
160
+ "border": f"1px solid {color('accent', 6)}",
161
+ "background_color": "var(--accent-surface)",
162
+ },
163
+ "&[data-variant='ghost']": {
164
+ "background_color": "none",
165
+ "box_shadow": "None",
166
+ },
167
+ "--animation-duration": f"{self.duration}ms",
168
+ "--animation-easing": self.easing,
169
+ }
170
+ if self.show_dividers is not None:
171
+ style["--divider-px"] = cond(self.show_dividers, "1px", "0")
172
+ else:
173
+ style["&[data-variant='outline']"]["--divider-px"] = "1px"
174
+ style["&[data-variant='surface']"]["--divider-px"] = "1px"
175
+ return Style(style)
468
176
 
469
177
 
470
178
  class AccordionItem(AccordionComponent):
@@ -488,13 +196,6 @@ class AccordionItem(AccordionComponent):
488
196
 
489
197
  _valid_parents: List[str] = ["AccordionRoot"]
490
198
 
491
- def _apply_theme(self, theme: Component):
492
- self.style = Style(
493
- {
494
- **self.style,
495
- }
496
- )
497
-
498
199
  @classmethod
499
200
  def create(
500
201
  cls,
@@ -506,9 +207,9 @@ class AccordionItem(AccordionComponent):
506
207
  """Create an accordion item.
507
208
 
508
209
  Args:
210
+ *children: The list of children to use if header and content are not provided.
509
211
  header: The header of the accordion item.
510
212
  content: The content of the accordion item.
511
- *children: The list of children to use if header and content are not provided.
512
213
  **props: Additional properties to apply to the accordion item.
513
214
 
514
215
  Returns:
@@ -522,19 +223,71 @@ class AccordionItem(AccordionComponent):
522
223
  ):
523
224
  cls_name = f"{cls_name} AccordionItem"
524
225
 
226
+ color_scheme = props.get("color_scheme")
227
+ variant = props.get("variant")
228
+
525
229
  if (header is not None) and (content is not None):
526
230
  children = [
527
231
  AccordionHeader.create(
528
232
  AccordionTrigger.create(
529
233
  header,
530
- AccordionIcon.create(),
234
+ AccordionIcon.create(
235
+ color_scheme=color_scheme,
236
+ variant=variant,
237
+ ),
238
+ color_scheme=color_scheme,
239
+ variant=variant,
531
240
  ),
241
+ color_scheme=color_scheme,
242
+ variant=variant,
243
+ ),
244
+ AccordionContent.create(
245
+ content,
246
+ color_scheme=color_scheme,
247
+ variant=variant,
532
248
  ),
533
- AccordionContent.create(content),
534
249
  ]
535
250
 
536
251
  return super().create(*children, value=value, **props, class_name=cls_name)
537
252
 
253
+ def add_style(self) -> Style | None:
254
+ """Add style to the component.
255
+
256
+ Returns:
257
+ The style of the component.
258
+ """
259
+ divider_style = f"var(--divider-px) solid {color('gray', 6, alpha=True)}"
260
+ return Style(
261
+ {
262
+ "overflow": "hidden",
263
+ "width": "100%",
264
+ "margin_top": "1px",
265
+ "border_top": divider_style,
266
+ "&:first-child": {
267
+ "margin_top": 0,
268
+ "border_top": 0,
269
+ "border_top_left_radius": "var(--radius-4)",
270
+ "border_top_right_radius": "var(--radius-4)",
271
+ },
272
+ "&:last-child": {
273
+ "border_bottom_left_radius": "var(--radius-4)",
274
+ "border_bottom_right_radius": "var(--radius-4)",
275
+ },
276
+ "&:focus-within": {
277
+ "position": "relative",
278
+ "z_index": 1,
279
+ },
280
+ _inherited_variant_selector("ghost", "&:first-child"): {
281
+ "border_radius": 0,
282
+ "border_top": divider_style,
283
+ },
284
+ _inherited_variant_selector("ghost", "&:last-child"): {
285
+ "border_radius": 0,
286
+ "border_bottom": divider_style,
287
+ },
288
+ }
289
+ )
290
+
538
291
 
539
292
  class AccordionHeader(AccordionComponent):
540
293
  """An accordion component."""
@@ -561,8 +314,13 @@ class AccordionHeader(AccordionComponent):
561
314
 
562
315
  return super().create(*children, class_name=cls_name, **props)
563
316
 
564
- def _apply_theme(self, theme: Component):
565
- self.style = Style({**self.style})
317
+ def add_style(self) -> Style | None:
318
+ """Add style to the component.
319
+
320
+ Returns:
321
+ The style of the component.
322
+ """
323
+ return Style({"display": "flex"})
566
324
 
567
325
 
568
326
  class AccordionTrigger(AccordionComponent):
@@ -590,8 +348,44 @@ class AccordionTrigger(AccordionComponent):
590
348
 
591
349
  return super().create(*children, class_name=cls_name, **props)
592
350
 
593
- def _apply_theme(self, theme: Component):
594
- self.style = Style({**self.style})
351
+ def add_style(self) -> Style | None:
352
+ """Add style to the component.
353
+
354
+ Returns:
355
+ The style of the component.
356
+ """
357
+ return Style(
358
+ {
359
+ "color": color("accent", 11),
360
+ "font_size": "1.1em",
361
+ "line_height": 1,
362
+ "justify_content": "space-between",
363
+ "align_items": "center",
364
+ "flex": 1,
365
+ "display": "flex",
366
+ "padding": "var(--space-3) var(--space-4)",
367
+ "width": "100%",
368
+ "box_shadow": f"0 var(--divider-px) 0 {color('gray', 6, alpha=True)}",
369
+ "&[data-state='open'] > .AccordionChevron": {
370
+ "transform": "rotate(180deg)",
371
+ },
372
+ "&:hover": {
373
+ "background_color": color("accent", 4),
374
+ },
375
+ "& > .AccordionChevron": {
376
+ "transition": f"transform var(--animation-duration) var(--animation-easing)",
377
+ },
378
+ _inherited_variant_selector("classic"): {
379
+ "color": "var(--accent-contrast)",
380
+ "&:hover": {
381
+ "background_color": color("accent", 10),
382
+ },
383
+ "& > .AccordionChevron": {
384
+ "color": "var(--accent-contrast)",
385
+ },
386
+ },
387
+ }
388
+ )
595
389
 
596
390
 
597
391
  class AccordionIcon(Icon):
@@ -623,6 +417,14 @@ class AccordionContent(AccordionComponent):
623
417
 
624
418
  alias = "RadixAccordionContent"
625
419
 
420
+ def add_imports(self) -> imports.ImportDict:
421
+ """Add imports to the component.
422
+
423
+ Returns:
424
+ The imports of the component.
425
+ """
426
+ return {"@emotion/react": [imports.ImportVar(tag="keyframes")]}
427
+
626
428
  @classmethod
627
429
  def create(cls, *children, **props) -> Component:
628
430
  """Create the Accordion content component.
@@ -641,14 +443,67 @@ class AccordionContent(AccordionComponent):
641
443
 
642
444
  return super().create(*children, class_name=cls_name, **props)
643
445
 
644
- def _apply_theme(self, theme: Component):
645
- self.style = Style({**self.style})
446
+ def add_custom_code(self) -> list[str]:
447
+ """Add custom code to the component.
448
+
449
+ Returns:
450
+ The custom code of the component.
451
+ """
452
+ return [
453
+ """
454
+ const slideDown = keyframes`
455
+ from {
456
+ height: 0;
457
+ }
458
+ to {
459
+ height: var(--radix-accordion-content-height);
460
+ }
461
+ `
462
+ const slideUp = keyframes`
463
+ from {
464
+ height: var(--radix-accordion-content-height);
465
+ }
466
+ to {
467
+ height: 0;
468
+ }
469
+ `
470
+ """
471
+ ]
472
+
473
+ def add_style(self) -> Style | None:
474
+ """Add style to the component.
475
+
476
+ Returns:
477
+ The style of the component.
478
+ """
479
+ slideDown = Var.create(
480
+ f"${{slideDown}} var(--animation-duration) var(--animation-easing)",
481
+ _var_is_string=True,
482
+ )
483
+
484
+ slideUp = Var.create(
485
+ f"${{slideUp}} var(--animation-duration) var(--animation-easing)",
486
+ _var_is_string=True,
487
+ )
646
488
 
647
- # def _get_imports(self):
648
- # return {
649
- # **super()._get_imports(),
650
- # "@emotion/react": [imports.ImportVar(tag="keyframes")],
651
- # }
489
+ return Style(
490
+ {
491
+ "overflow": "hidden",
492
+ "color": color("accent", 11),
493
+ "padding_x": "var(--space-4)",
494
+ # Apply before and after content to avoid height animation jank.
495
+ "&:before, &:after": {
496
+ "content": "' '",
497
+ "display": "block",
498
+ "height": "var(--space-3)",
499
+ },
500
+ "&[data-state='open']": {"animation": slideDown},
501
+ "&[data-state='closed']": {"animation": slideUp},
502
+ _inherited_variant_selector("classic"): {
503
+ "color": "var(--accent-contrast)",
504
+ },
505
+ }
506
+ )
652
507
 
653
508
 
654
509
  class Accordion(ComponentNamespace):