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.
- reflex/.templates/apps/blank/code/blank.py +19 -16
- reflex/.templates/apps/demo/code/demo.py +1 -1
- reflex/.templates/apps/demo/code/pages/datatable.py +4 -4
- reflex/.templates/apps/demo/code/pages/forms.py +2 -2
- reflex/.templates/jinja/web/tailwind.config.js.jinja2 +12 -0
- reflex/.templates/web/utils/helpers/debounce.js +17 -0
- reflex/.templates/web/utils/helpers/throttle.js +22 -0
- reflex/.templates/web/utils/state.js +21 -3
- reflex/__init__.py +6 -1
- reflex/__init__.pyi +4 -1
- reflex/app.py +160 -140
- reflex/app_module_for_backend.py +1 -1
- reflex/base.py +13 -15
- reflex/compiler/compiler.py +10 -1
- reflex/compiler/utils.py +3 -30
- reflex/components/__init__.py +1 -0
- reflex/components/chakra/datadisplay/list.py +1 -3
- reflex/components/chakra/datadisplay/list.pyi +3 -3
- reflex/components/chakra/disclosure/accordion.py +1 -1
- reflex/components/chakra/forms/pininput.pyi +1 -1
- reflex/components/chakra/media/icon.py +2 -2
- reflex/components/component.py +275 -32
- reflex/components/core/__init__.py +2 -2
- reflex/components/core/cond.py +1 -10
- reflex/components/core/debounce.py +5 -2
- reflex/components/core/debounce.pyi +4 -2
- reflex/components/core/foreach.py +60 -49
- reflex/components/core/html.py +6 -0
- reflex/components/core/match.py +2 -17
- reflex/components/core/upload.py +42 -1
- reflex/components/core/upload.pyi +199 -1
- reflex/components/datadisplay/code.py +7 -3
- reflex/components/datadisplay/code.pyi +3 -1
- reflex/components/el/elements/forms.py +1 -1
- reflex/components/el/elements/forms.pyi +1 -1
- reflex/components/lucide/icon.py +5 -13
- reflex/components/lucide/icon.pyi +0 -1
- reflex/components/markdown/markdown.py +5 -23
- reflex/components/markdown/markdown.pyi +1 -4
- reflex/components/radix/primitives/accordion.py +265 -410
- reflex/components/radix/primitives/accordion.pyi +390 -36
- reflex/components/radix/primitives/form.py +33 -29
- reflex/components/radix/primitives/form.pyi +7 -2
- reflex/components/radix/primitives/progress.py +17 -9
- reflex/components/radix/primitives/progress.pyi +2 -0
- reflex/components/radix/primitives/slider.py +30 -18
- reflex/components/radix/primitives/slider.pyi +4 -0
- reflex/components/radix/themes/base.py +8 -1
- reflex/components/radix/themes/base.pyi +79 -1
- reflex/components/radix/themes/color_mode.py +88 -20
- reflex/components/radix/themes/color_mode.pyi +157 -139
- reflex/components/radix/themes/components/__init__.py +17 -0
- reflex/components/radix/themes/components/badge.py +2 -1
- reflex/components/radix/themes/components/badge.pyi +3 -1
- reflex/components/radix/themes/components/button.py +3 -1
- reflex/components/radix/themes/components/button.pyi +4 -1
- reflex/components/radix/themes/components/checkbox_cards.py +48 -0
- reflex/components/radix/themes/components/checkbox_cards.pyi +264 -0
- reflex/components/radix/themes/components/checkbox_group.py +42 -0
- reflex/components/radix/themes/components/checkbox_group.pyi +253 -0
- reflex/components/radix/themes/components/data_list.py +63 -0
- reflex/components/radix/themes/components/data_list.pyi +426 -0
- reflex/components/radix/themes/components/icon_button.py +20 -17
- reflex/components/radix/themes/components/icon_button.pyi +5 -1
- reflex/components/radix/themes/components/progress.py +55 -0
- reflex/components/radix/themes/components/progress.pyi +180 -0
- reflex/components/radix/themes/components/radio.py +31 -0
- reflex/components/radix/themes/components/radio.pyi +169 -0
- reflex/components/radix/themes/components/radio_cards.py +48 -0
- reflex/components/radix/themes/components/radio_cards.pyi +264 -0
- reflex/components/radix/themes/components/radio_group.py +2 -4
- reflex/components/radix/themes/components/segmented_control.py +48 -0
- reflex/components/radix/themes/components/segmented_control.pyi +262 -0
- reflex/components/radix/themes/components/skeleton.py +32 -0
- reflex/components/radix/themes/components/skeleton.pyi +106 -0
- reflex/components/radix/themes/components/spinner.py +26 -0
- reflex/components/radix/themes/components/spinner.pyi +101 -0
- reflex/components/radix/themes/components/tabs.py +26 -1
- reflex/components/radix/themes/components/tabs.pyi +69 -9
- reflex/components/radix/themes/components/text_field.py +101 -71
- reflex/components/radix/themes/components/text_field.pyi +81 -499
- reflex/components/radix/themes/layout/base.py +2 -2
- reflex/components/radix/themes/layout/base.pyi +4 -4
- reflex/components/radix/themes/layout/center.py +8 -3
- reflex/components/radix/themes/layout/center.pyi +2 -1
- reflex/components/radix/themes/layout/container.py +30 -2
- reflex/components/radix/themes/layout/container.pyi +9 -30
- reflex/components/radix/themes/layout/list.py +10 -5
- reflex/components/radix/themes/layout/list.pyi +5 -21
- reflex/components/radix/themes/layout/spacer.py +8 -3
- reflex/components/radix/themes/layout/spacer.pyi +2 -1
- reflex/components/radix/themes/layout/stack.py +7 -1
- reflex/components/radix/themes/layout/stack.pyi +3 -3
- reflex/components/radix/themes/typography/link.py +10 -2
- reflex/components/radix/themes/typography/link.pyi +5 -4
- reflex/components/sonner/__init__.py +3 -0
- reflex/components/sonner/toast.py +267 -0
- reflex/components/sonner/toast.pyi +205 -0
- reflex/components/tags/iter_tag.py +9 -6
- reflex/config.py +30 -54
- reflex/constants/__init__.py +0 -2
- reflex/constants/base.py +0 -5
- reflex/constants/colors.py +2 -0
- reflex/constants/installer.py +6 -1
- reflex/constants/route.py +4 -0
- reflex/custom_components/custom_components.py +24 -2
- reflex/event.py +75 -30
- reflex/experimental/__init__.py +5 -0
- reflex/experimental/layout.py +24 -6
- reflex/model.py +2 -1
- reflex/page.py +7 -4
- reflex/reflex.py +8 -3
- reflex/route.py +39 -0
- reflex/state.py +128 -131
- reflex/style.py +25 -3
- reflex/testing.py +10 -6
- reflex/utils/console.py +3 -1
- reflex/utils/exec.py +20 -7
- reflex/utils/format.py +1 -1
- reflex/utils/imports.py +3 -1
- reflex/utils/prerequisites.py +141 -20
- reflex/utils/processes.py +21 -1
- reflex/utils/pyi_generator.py +100 -5
- reflex/utils/serializers.py +1 -1
- reflex/utils/telemetry.py +26 -4
- reflex/utils/types.py +62 -18
- reflex/vars.py +11 -5
- {reflex-0.4.9a1.dist-info → reflex-0.5.0.dist-info}/METADATA +16 -4
- {reflex-0.4.9a1.dist-info → reflex-0.5.0.dist-info}/RECORD +132 -110
- {reflex-0.4.9a1.dist-info → reflex-0.5.0.dist-info}/WHEEL +1 -1
- reflex/app.pyi +0 -149
- {reflex-0.4.9a1.dist-info → reflex-0.5.0.dist-info}/LICENSE +0 -0
- {reflex-0.4.9a1.dist-info → reflex-0.5.0.dist-info}/entry_points.txt +0 -0
reflex/compiler/utils.py
CHANGED
|
@@ -3,19 +3,13 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
|
-
from typing import
|
|
6
|
+
from typing import Any, Callable, Dict, Optional, Type, Union
|
|
7
7
|
from urllib.parse import urlparse
|
|
8
8
|
|
|
9
9
|
try:
|
|
10
|
-
|
|
11
|
-
# reflex-hosting-cli tools are compatible with pydantic v2
|
|
12
|
-
|
|
13
|
-
if not TYPE_CHECKING:
|
|
14
|
-
from pydantic.v1.fields import ModelField
|
|
15
|
-
else:
|
|
16
|
-
raise ModuleNotFoundError
|
|
10
|
+
from pydantic.v1.fields import ModelField
|
|
17
11
|
except ModuleNotFoundError:
|
|
18
|
-
from pydantic.fields import ModelField
|
|
12
|
+
from pydantic.fields import ModelField # type: ignore
|
|
19
13
|
|
|
20
14
|
from reflex import constants
|
|
21
15
|
from reflex.components.base import (
|
|
@@ -392,27 +386,6 @@ def get_stateful_components_path() -> str:
|
|
|
392
386
|
)
|
|
393
387
|
|
|
394
388
|
|
|
395
|
-
def get_asset_path(filename: str | None = None) -> str:
|
|
396
|
-
"""Get the path for an asset.
|
|
397
|
-
|
|
398
|
-
Args:
|
|
399
|
-
filename: If given, is added to the root path of assets dir.
|
|
400
|
-
|
|
401
|
-
Returns:
|
|
402
|
-
The path of the asset.
|
|
403
|
-
"""
|
|
404
|
-
console.deprecate(
|
|
405
|
-
feature_name="rx.get_asset_path",
|
|
406
|
-
reason="use rx.get_upload_dir() instead.",
|
|
407
|
-
deprecation_version="0.4.0",
|
|
408
|
-
removal_version="0.5.0",
|
|
409
|
-
)
|
|
410
|
-
if filename is None:
|
|
411
|
-
return constants.Dirs.WEB_ASSETS
|
|
412
|
-
else:
|
|
413
|
-
return os.path.join(constants.Dirs.WEB_ASSETS, filename)
|
|
414
|
-
|
|
415
|
-
|
|
416
389
|
def add_meta(
|
|
417
390
|
page: Component,
|
|
418
391
|
title: str,
|
reflex/components/__init__.py
CHANGED
|
@@ -23,9 +23,7 @@ class List(ChakraComponent):
|
|
|
23
23
|
style_type: Var[str]
|
|
24
24
|
|
|
25
25
|
@classmethod
|
|
26
|
-
def create(
|
|
27
|
-
cls, *children, items: list | Var[list] | None = None, **props
|
|
28
|
-
) -> Component:
|
|
26
|
+
def create(cls, *children, items: Var[list] | None = None, **props) -> Component:
|
|
29
27
|
"""Create a list component.
|
|
30
28
|
|
|
31
29
|
Args:
|
|
@@ -18,7 +18,7 @@ class List(ChakraComponent):
|
|
|
18
18
|
def create( # type: ignore
|
|
19
19
|
cls,
|
|
20
20
|
*children,
|
|
21
|
-
items: Optional[
|
|
21
|
+
items: Optional[Union[Var[list], list]] = None,
|
|
22
22
|
spacing: Optional[Union[Var[str], str]] = None,
|
|
23
23
|
style_position: Optional[Union[Var[str], str]] = None,
|
|
24
24
|
style_type: Optional[Union[Var[str], str]] = None,
|
|
@@ -178,7 +178,7 @@ class OrderedList(List):
|
|
|
178
178
|
def create( # type: ignore
|
|
179
179
|
cls,
|
|
180
180
|
*children,
|
|
181
|
-
items: Optional[
|
|
181
|
+
items: Optional[Union[Var[list], list]] = None,
|
|
182
182
|
spacing: Optional[Union[Var[str], str]] = None,
|
|
183
183
|
style_position: Optional[Union[Var[str], str]] = None,
|
|
184
184
|
style_type: Optional[Union[Var[str], str]] = None,
|
|
@@ -262,7 +262,7 @@ class UnorderedList(List):
|
|
|
262
262
|
def create( # type: ignore
|
|
263
263
|
cls,
|
|
264
264
|
*children,
|
|
265
|
-
items: Optional[
|
|
265
|
+
items: Optional[Union[Var[list], list]] = None,
|
|
266
266
|
spacing: Optional[Union[Var[str], str]] = None,
|
|
267
267
|
style_position: Optional[Union[Var[str], str]] = None,
|
|
268
268
|
style_type: Optional[Union[Var[str], str]] = None,
|
|
@@ -147,7 +147,7 @@ class PinInputField(ChakraComponent):
|
|
|
147
147
|
def create( # type: ignore
|
|
148
148
|
cls,
|
|
149
149
|
*children,
|
|
150
|
-
index: Optional[Var[int]] = None,
|
|
150
|
+
index: Optional[Union[Var[int], int]] = None,
|
|
151
151
|
name: Optional[Union[Var[str], str]] = None,
|
|
152
152
|
style: Optional[Style] = None,
|
|
153
153
|
key: Optional[Any] = None,
|
|
@@ -37,9 +37,9 @@ class Icon(ChakraIconComponent):
|
|
|
37
37
|
raise AttributeError(
|
|
38
38
|
f"Passing children to Icon component is not allowed: remove positional arguments {children} to fix"
|
|
39
39
|
)
|
|
40
|
-
if "tag" not in props
|
|
40
|
+
if "tag" not in props:
|
|
41
41
|
raise AttributeError("Missing 'tag' keyword-argument for Icon")
|
|
42
|
-
if
|
|
42
|
+
if not isinstance(props["tag"], str) or props["tag"].lower() not in ICON_LIST:
|
|
43
43
|
raise ValueError(
|
|
44
44
|
f"Invalid icon tag: {props['tag']}. Please use one of the following: {sorted(ICON_LIST)}"
|
|
45
45
|
)
|
reflex/components/component.py
CHANGED
|
@@ -213,6 +213,91 @@ class Component(BaseComponent, ABC):
|
|
|
213
213
|
# State class associated with this component instance
|
|
214
214
|
State: Optional[Type[reflex.state.State]] = None
|
|
215
215
|
|
|
216
|
+
def add_imports(self) -> dict[str, str | ImportVar | list[str | ImportVar]]:
|
|
217
|
+
"""Add imports for the component.
|
|
218
|
+
|
|
219
|
+
This method should be implemented by subclasses to add new imports for the component.
|
|
220
|
+
|
|
221
|
+
Implementations do NOT need to call super(). The result of calling
|
|
222
|
+
add_imports in each parent class will be merged internally.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
The additional imports for this component subclass.
|
|
226
|
+
|
|
227
|
+
The format of the return value is a dictionary where the keys are the
|
|
228
|
+
library names (with optional npm-style version specifications) mapping
|
|
229
|
+
to a single name to be imported, or a list names to be imported.
|
|
230
|
+
|
|
231
|
+
For advanced use cases, the values can be ImportVar instances (for
|
|
232
|
+
example, to provide an alias or mark that an import is the default
|
|
233
|
+
export from the given library).
|
|
234
|
+
|
|
235
|
+
```python
|
|
236
|
+
return {
|
|
237
|
+
"react": "useEffect",
|
|
238
|
+
"react-draggable": ["DraggableCore", rx.ImportVar(tag="Draggable", is_default=True)],
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
"""
|
|
242
|
+
return {}
|
|
243
|
+
|
|
244
|
+
def add_hooks(self) -> list[str]:
|
|
245
|
+
"""Add hooks inside the component function.
|
|
246
|
+
|
|
247
|
+
Hooks are pieces of literal Javascript code that is inserted inside the
|
|
248
|
+
React component function.
|
|
249
|
+
|
|
250
|
+
Each logical hook should be a separate string in the list.
|
|
251
|
+
|
|
252
|
+
Common strings will be deduplicated and inserted into the component
|
|
253
|
+
function only once, so define const variables and other identical code
|
|
254
|
+
in their own strings to avoid defining the same const or hook multiple
|
|
255
|
+
times.
|
|
256
|
+
|
|
257
|
+
If a hook depends on specific data from the component instance, be sure
|
|
258
|
+
to use unique values inside the string to _avoid_ deduplication.
|
|
259
|
+
|
|
260
|
+
Implementations do NOT need to call super(). The result of calling
|
|
261
|
+
add_hooks in each parent class will be merged and deduplicated internally.
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
The additional hooks for this component subclass.
|
|
265
|
+
|
|
266
|
+
```python
|
|
267
|
+
return [
|
|
268
|
+
"const [count, setCount] = useState(0);",
|
|
269
|
+
"useEffect(() => { setCount((prev) => prev + 1); console.log(`mounted ${count} times`); }, []);",
|
|
270
|
+
]
|
|
271
|
+
```
|
|
272
|
+
"""
|
|
273
|
+
return []
|
|
274
|
+
|
|
275
|
+
def add_custom_code(self) -> list[str]:
|
|
276
|
+
"""Add custom Javascript code into the page that contains this component.
|
|
277
|
+
|
|
278
|
+
Custom code is inserted at module level, after any imports.
|
|
279
|
+
|
|
280
|
+
Each string of custom code is deduplicated per-page, so take care to
|
|
281
|
+
avoid defining the same const or function differently from different
|
|
282
|
+
component instances.
|
|
283
|
+
|
|
284
|
+
Custom code is useful for defining global functions or constants which
|
|
285
|
+
can then be referenced inside hooks or used by component vars.
|
|
286
|
+
|
|
287
|
+
Implementations do NOT need to call super(). The result of calling
|
|
288
|
+
add_custom_code in each parent class will be merged and deduplicated internally.
|
|
289
|
+
|
|
290
|
+
Returns:
|
|
291
|
+
The additional custom code for this component subclass.
|
|
292
|
+
|
|
293
|
+
```python
|
|
294
|
+
return [
|
|
295
|
+
"const translatePoints = (event) => { return { x: event.clientX, y: event.clientY }; };",
|
|
296
|
+
]
|
|
297
|
+
```
|
|
298
|
+
"""
|
|
299
|
+
return []
|
|
300
|
+
|
|
216
301
|
@classmethod
|
|
217
302
|
def __init_subclass__(cls, **kwargs):
|
|
218
303
|
"""Set default properties.
|
|
@@ -255,6 +340,7 @@ class Component(BaseComponent, ABC):
|
|
|
255
340
|
|
|
256
341
|
Raises:
|
|
257
342
|
TypeError: If an invalid prop is passed.
|
|
343
|
+
ValueError: If an event trigger passed is not valid.
|
|
258
344
|
"""
|
|
259
345
|
# Set the id and children initially.
|
|
260
346
|
children = kwargs.get("children", [])
|
|
@@ -284,6 +370,12 @@ class Component(BaseComponent, ABC):
|
|
|
284
370
|
|
|
285
371
|
# Iterate through the kwargs and set the props.
|
|
286
372
|
for key, value in kwargs.items():
|
|
373
|
+
if key.startswith("on_") and key not in triggers and key not in props:
|
|
374
|
+
raise ValueError(
|
|
375
|
+
f"The {(comp_name := type(self).__name__)} does not take in an `{key}` event trigger. If {comp_name}"
|
|
376
|
+
f" is a third party component make sure to add `{key}` to the component's event triggers. "
|
|
377
|
+
f"visit https://reflex.dev/docs/wrapping-react/logic/#event-triggers for more info."
|
|
378
|
+
)
|
|
287
379
|
if key in triggers:
|
|
288
380
|
# Event triggers are bound to event chains.
|
|
289
381
|
field_type = EventChain
|
|
@@ -296,6 +388,8 @@ class Component(BaseComponent, ABC):
|
|
|
296
388
|
|
|
297
389
|
# Check whether the key is a component prop.
|
|
298
390
|
if types._issubclass(field_type, Var):
|
|
391
|
+
# Used to store the passed types if var type is a union.
|
|
392
|
+
passed_types = None
|
|
299
393
|
try:
|
|
300
394
|
# Try to create a var from the value.
|
|
301
395
|
kwargs[key] = Var.create(value)
|
|
@@ -320,10 +414,25 @@ class Component(BaseComponent, ABC):
|
|
|
320
414
|
# If it is not a valid var, check the base types.
|
|
321
415
|
passed_type = type(value)
|
|
322
416
|
expected_type = fields[key].outer_type_
|
|
323
|
-
if
|
|
417
|
+
if types.is_union(passed_type):
|
|
418
|
+
# We need to check all possible types in the union.
|
|
419
|
+
passed_types = (
|
|
420
|
+
arg for arg in passed_type.__args__ if arg is not type(None)
|
|
421
|
+
)
|
|
422
|
+
if (
|
|
423
|
+
# If the passed var is a union, check if all possible types are valid.
|
|
424
|
+
passed_types
|
|
425
|
+
and not all(
|
|
426
|
+
types._issubclass(pt, expected_type) for pt in passed_types
|
|
427
|
+
)
|
|
428
|
+
) or (
|
|
429
|
+
# Else just check if the passed var type is valid.
|
|
430
|
+
not passed_types
|
|
431
|
+
and not types._issubclass(passed_type, expected_type)
|
|
432
|
+
):
|
|
324
433
|
value_name = value._var_name if isinstance(value, Var) else value
|
|
325
434
|
raise TypeError(
|
|
326
|
-
f"Invalid var passed for prop {key}, expected type {expected_type}, got value {value_name} of type {passed_type}."
|
|
435
|
+
f"Invalid var passed for prop {type(self).__name__}.{key}, expected type {expected_type}, got value {value_name} of type {passed_types or passed_type}."
|
|
327
436
|
)
|
|
328
437
|
|
|
329
438
|
# Check if the key is an event trigger.
|
|
@@ -397,7 +506,7 @@ class Component(BaseComponent, ABC):
|
|
|
397
506
|
if isinstance(value, List):
|
|
398
507
|
events: list[EventSpec] = []
|
|
399
508
|
for v in value:
|
|
400
|
-
if isinstance(v, EventHandler):
|
|
509
|
+
if isinstance(v, (EventHandler, EventSpec)):
|
|
401
510
|
# Call the event handler to get the event.
|
|
402
511
|
try:
|
|
403
512
|
event = call_event_handler(v, args_spec)
|
|
@@ -408,9 +517,6 @@ class Component(BaseComponent, ABC):
|
|
|
408
517
|
|
|
409
518
|
# Add the event to the chain.
|
|
410
519
|
events.append(event)
|
|
411
|
-
elif isinstance(v, EventSpec):
|
|
412
|
-
# Add the event to the chain.
|
|
413
|
-
events.append(v)
|
|
414
520
|
elif isinstance(v, Callable):
|
|
415
521
|
# Call the lambda to get the event chain.
|
|
416
522
|
events.extend(call_event_fn(v, args_spec))
|
|
@@ -502,6 +608,8 @@ class Component(BaseComponent, ABC):
|
|
|
502
608
|
def _apply_theme(self, theme: Optional[Component]):
|
|
503
609
|
"""Apply the theme to this component.
|
|
504
610
|
|
|
611
|
+
Deprecated. Use add_style instead.
|
|
612
|
+
|
|
505
613
|
Args:
|
|
506
614
|
theme: The theme to apply.
|
|
507
615
|
"""
|
|
@@ -635,7 +743,7 @@ class Component(BaseComponent, ABC):
|
|
|
635
743
|
f"Underscore suffix for prop `{under_prop}`",
|
|
636
744
|
reason=f"for consistency. Use `{prop}` instead.",
|
|
637
745
|
deprecation_version="0.4.0",
|
|
638
|
-
removal_version="0.
|
|
746
|
+
removal_version="0.6.0",
|
|
639
747
|
dedupe=False,
|
|
640
748
|
)
|
|
641
749
|
props[prop] = props.pop(under_prop)
|
|
@@ -673,44 +781,115 @@ class Component(BaseComponent, ABC):
|
|
|
673
781
|
|
|
674
782
|
return cls(children=children, **props)
|
|
675
783
|
|
|
676
|
-
def
|
|
677
|
-
"""Add
|
|
784
|
+
def add_style(self) -> dict[str, Any] | None:
|
|
785
|
+
"""Add style to the component.
|
|
678
786
|
|
|
679
|
-
|
|
680
|
-
|
|
787
|
+
Downstream components can override this method to return a style dict
|
|
788
|
+
that will be applied to the component.
|
|
789
|
+
|
|
790
|
+
Returns:
|
|
791
|
+
The style to add.
|
|
792
|
+
"""
|
|
793
|
+
return None
|
|
794
|
+
|
|
795
|
+
def _add_style(self) -> Style:
|
|
796
|
+
"""Call add_style for all bases in the MRO.
|
|
797
|
+
|
|
798
|
+
Downstream components should NOT override. Use add_style instead.
|
|
799
|
+
|
|
800
|
+
Returns:
|
|
801
|
+
The style to add.
|
|
681
802
|
"""
|
|
682
|
-
|
|
803
|
+
styles = []
|
|
804
|
+
|
|
805
|
+
# Walk the MRO to call all `add_style` methods.
|
|
806
|
+
for base in self._iter_parent_classes_with_method("add_style"):
|
|
807
|
+
s = base.add_style(self) # type: ignore
|
|
808
|
+
if s is not None:
|
|
809
|
+
styles.append(s)
|
|
810
|
+
|
|
811
|
+
_style = Style()
|
|
812
|
+
for s in reversed(styles):
|
|
813
|
+
_style.update(s)
|
|
814
|
+
return _style
|
|
683
815
|
|
|
684
|
-
def
|
|
816
|
+
def _get_component_style(self, styles: ComponentStyle) -> Style | None:
|
|
817
|
+
"""Get the style to the component from `App.style`.
|
|
818
|
+
|
|
819
|
+
Args:
|
|
820
|
+
styles: The style to apply.
|
|
821
|
+
|
|
822
|
+
Returns:
|
|
823
|
+
The style of the component.
|
|
824
|
+
"""
|
|
825
|
+
component_style = None
|
|
826
|
+
if type(self) in styles:
|
|
827
|
+
component_style = Style(styles[type(self)])
|
|
828
|
+
if self.create in styles:
|
|
829
|
+
component_style = Style(styles[self.create])
|
|
830
|
+
return component_style
|
|
831
|
+
|
|
832
|
+
def _add_style_recursive(
|
|
833
|
+
self, style: ComponentStyle, theme: Optional[Component] = None
|
|
834
|
+
) -> Component:
|
|
685
835
|
"""Add additional style to the component and its children.
|
|
686
836
|
|
|
837
|
+
Apply order is as follows (with the latest overriding the earliest):
|
|
838
|
+
1. Default style from `_add_style`/`add_style`.
|
|
839
|
+
2. User-defined style from `App.style`.
|
|
840
|
+
3. User-defined style from `Component.style`.
|
|
841
|
+
4. style dict and css props passed to the component instance.
|
|
842
|
+
|
|
687
843
|
Args:
|
|
688
844
|
style: A dict from component to styling.
|
|
845
|
+
theme: The theme to apply. (for retro-compatibility with deprecated _apply_theme API)
|
|
846
|
+
|
|
847
|
+
Raises:
|
|
848
|
+
UserWarning: If `_add_style` has been overridden.
|
|
689
849
|
|
|
690
850
|
Returns:
|
|
691
851
|
The component with the additional style.
|
|
692
852
|
"""
|
|
693
|
-
|
|
694
|
-
if type(self)
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
853
|
+
# 1. Default style from `_add_style`/`add_style`.
|
|
854
|
+
if type(self)._add_style != Component._add_style:
|
|
855
|
+
raise UserWarning(
|
|
856
|
+
"Do not override _add_style directly. Use add_style instead."
|
|
857
|
+
)
|
|
858
|
+
new_style = self._add_style()
|
|
859
|
+
style_vars = [new_style._var_data]
|
|
860
|
+
|
|
861
|
+
# 2. User-defined style from `App.style`.
|
|
862
|
+
component_style = self._get_component_style(style)
|
|
863
|
+
if component_style:
|
|
864
|
+
new_style.update(component_style)
|
|
865
|
+
style_vars.append(component_style._var_data)
|
|
866
|
+
|
|
867
|
+
# 3. User-defined style from `Component.style`.
|
|
868
|
+
# Apply theme for retro-compatibility with deprecated _apply_theme API
|
|
869
|
+
if type(self)._apply_theme != Component._apply_theme:
|
|
870
|
+
console.deprecate(
|
|
871
|
+
f"{self.__class__.__name__}._apply_theme",
|
|
872
|
+
reason="use add_style instead",
|
|
873
|
+
deprecation_version="0.5.0",
|
|
874
|
+
removal_version="0.6.0",
|
|
875
|
+
)
|
|
876
|
+
self._apply_theme(theme)
|
|
877
|
+
|
|
878
|
+
# 4. style dict and css props passed to the component instance.
|
|
879
|
+
new_style.update(self.style)
|
|
880
|
+
style_vars.append(self.style._var_data)
|
|
881
|
+
|
|
882
|
+
new_style._var_data = VarData.merge(*style_vars)
|
|
704
883
|
|
|
705
|
-
|
|
706
|
-
|
|
884
|
+
# Assign the new style
|
|
885
|
+
self.style = new_style
|
|
707
886
|
|
|
708
887
|
# Recursively add style to the children.
|
|
709
888
|
for child in self.children:
|
|
710
889
|
# Skip BaseComponent and StatefulComponent children.
|
|
711
890
|
if not isinstance(child, Component):
|
|
712
891
|
continue
|
|
713
|
-
child._add_style_recursive(style)
|
|
892
|
+
child._add_style_recursive(style, theme)
|
|
714
893
|
return self
|
|
715
894
|
|
|
716
895
|
def _get_style(self) -> dict:
|
|
@@ -925,6 +1104,30 @@ class Component(BaseComponent, ABC):
|
|
|
925
1104
|
return True
|
|
926
1105
|
return False
|
|
927
1106
|
|
|
1107
|
+
@classmethod
|
|
1108
|
+
def _iter_parent_classes_with_method(cls, method: str) -> Iterator[Type[Component]]:
|
|
1109
|
+
"""Iterate through parent classes that define a given method.
|
|
1110
|
+
|
|
1111
|
+
Used for handling the `add_*` API functions that internally simulate a super() call chain.
|
|
1112
|
+
|
|
1113
|
+
Args:
|
|
1114
|
+
method: The method to look for.
|
|
1115
|
+
|
|
1116
|
+
Yields:
|
|
1117
|
+
The parent classes that define the method (differently than the base).
|
|
1118
|
+
"""
|
|
1119
|
+
seen_methods = set([getattr(Component, method)])
|
|
1120
|
+
for clz in cls.mro():
|
|
1121
|
+
if clz is Component:
|
|
1122
|
+
break
|
|
1123
|
+
if not issubclass(clz, Component):
|
|
1124
|
+
continue
|
|
1125
|
+
method_func = getattr(clz, method, None)
|
|
1126
|
+
if not callable(method_func) or method_func in seen_methods:
|
|
1127
|
+
continue
|
|
1128
|
+
seen_methods.add(method_func)
|
|
1129
|
+
yield clz
|
|
1130
|
+
|
|
928
1131
|
def _get_custom_code(self) -> str | None:
|
|
929
1132
|
"""Get custom code for the component.
|
|
930
1133
|
|
|
@@ -947,6 +1150,11 @@ class Component(BaseComponent, ABC):
|
|
|
947
1150
|
if custom_code is not None:
|
|
948
1151
|
code.add(custom_code)
|
|
949
1152
|
|
|
1153
|
+
# Add the custom code from add_custom_code method.
|
|
1154
|
+
for clz in self._iter_parent_classes_with_method("add_custom_code"):
|
|
1155
|
+
for item in clz.add_custom_code(self):
|
|
1156
|
+
code.add(item)
|
|
1157
|
+
|
|
950
1158
|
# Add the custom code for the children.
|
|
951
1159
|
for child in self.children:
|
|
952
1160
|
code |= child._get_all_custom_code()
|
|
@@ -1082,6 +1290,26 @@ class Component(BaseComponent, ABC):
|
|
|
1082
1290
|
var._var_data.imports for var in self._get_vars() if var._var_data
|
|
1083
1291
|
]
|
|
1084
1292
|
|
|
1293
|
+
# If any subclass implements add_imports, merge the imports.
|
|
1294
|
+
def _make_list(
|
|
1295
|
+
value: str | ImportVar | list[str | ImportVar],
|
|
1296
|
+
) -> list[str | ImportVar]:
|
|
1297
|
+
if isinstance(value, (str, ImportVar)):
|
|
1298
|
+
return [value]
|
|
1299
|
+
return value
|
|
1300
|
+
|
|
1301
|
+
_added_import_dicts = []
|
|
1302
|
+
for clz in self._iter_parent_classes_with_method("add_imports"):
|
|
1303
|
+
_added_import_dicts.append(
|
|
1304
|
+
{
|
|
1305
|
+
package: [
|
|
1306
|
+
ImportVar(tag=tag) if not isinstance(tag, ImportVar) else tag
|
|
1307
|
+
for tag in _make_list(maybe_tags)
|
|
1308
|
+
]
|
|
1309
|
+
for package, maybe_tags in clz.add_imports(self).items()
|
|
1310
|
+
}
|
|
1311
|
+
)
|
|
1312
|
+
|
|
1085
1313
|
return imports.merge_imports(
|
|
1086
1314
|
*self._get_props_imports(),
|
|
1087
1315
|
self._get_dependencies_imports(),
|
|
@@ -1089,6 +1317,7 @@ class Component(BaseComponent, ABC):
|
|
|
1089
1317
|
_imports,
|
|
1090
1318
|
event_imports,
|
|
1091
1319
|
*var_imports,
|
|
1320
|
+
*_added_import_dicts,
|
|
1092
1321
|
)
|
|
1093
1322
|
|
|
1094
1323
|
def _get_all_imports(self, collapse: bool = False) -> imports.ImportDict:
|
|
@@ -1224,6 +1453,12 @@ class Component(BaseComponent, ABC):
|
|
|
1224
1453
|
if hooks is not None:
|
|
1225
1454
|
code[hooks] = None
|
|
1226
1455
|
|
|
1456
|
+
# Add the hook code from add_hooks for each parent class (this is reversed to preserve
|
|
1457
|
+
# the order of the hooks in the final output)
|
|
1458
|
+
for clz in reversed(tuple(self._iter_parent_classes_with_method("add_hooks"))):
|
|
1459
|
+
for hook in clz.add_hooks(self):
|
|
1460
|
+
code[hook] = None
|
|
1461
|
+
|
|
1227
1462
|
# Add the hook code for the children.
|
|
1228
1463
|
for child in self.children:
|
|
1229
1464
|
code = {**code, **child._get_all_hooks()}
|
|
@@ -1397,7 +1632,7 @@ class CustomComponent(Component):
|
|
|
1397
1632
|
else:
|
|
1398
1633
|
value = base_value
|
|
1399
1634
|
else:
|
|
1400
|
-
value = Var.create(value, _var_is_string=
|
|
1635
|
+
value = Var.create(value, _var_is_string=isinstance(value, str))
|
|
1401
1636
|
|
|
1402
1637
|
# Set the prop.
|
|
1403
1638
|
self.props[format.to_camel_case(key)] = value
|
|
@@ -1516,7 +1751,7 @@ class CustomComponent(Component):
|
|
|
1516
1751
|
|
|
1517
1752
|
|
|
1518
1753
|
def custom_component(
|
|
1519
|
-
component_fn: Callable[..., Component]
|
|
1754
|
+
component_fn: Callable[..., Component],
|
|
1520
1755
|
) -> Callable[..., CustomComponent]:
|
|
1521
1756
|
"""Create a custom component from a function.
|
|
1522
1757
|
|
|
@@ -1583,9 +1818,7 @@ class NoSSRComponent(Component):
|
|
|
1583
1818
|
library_import = f"const {self.alias if self.alias else self.tag} = dynamic(() => import('{import_name}')"
|
|
1584
1819
|
mod_import = (
|
|
1585
1820
|
# https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading#with-named-exports
|
|
1586
|
-
f".then((mod) => mod.{self.tag})"
|
|
1587
|
-
if not self.is_default
|
|
1588
|
-
else ""
|
|
1821
|
+
f".then((mod) => mod.{self.tag})" if not self.is_default else ""
|
|
1589
1822
|
)
|
|
1590
1823
|
return "".join((library_import, mod_import, opts_fragment))
|
|
1591
1824
|
|
|
@@ -1940,6 +2173,16 @@ class StatefulComponent(BaseComponent):
|
|
|
1940
2173
|
"""
|
|
1941
2174
|
return dict(Tag(name=self.tag))
|
|
1942
2175
|
|
|
2176
|
+
def __str__(self) -> str:
|
|
2177
|
+
"""Represent the component in React.
|
|
2178
|
+
|
|
2179
|
+
Returns:
|
|
2180
|
+
The code to render the component.
|
|
2181
|
+
"""
|
|
2182
|
+
from reflex.compiler.compiler import _compile_component
|
|
2183
|
+
|
|
2184
|
+
return _compile_component(self)
|
|
2185
|
+
|
|
1943
2186
|
@classmethod
|
|
1944
2187
|
def compile_from(cls, component: BaseComponent) -> BaseComponent:
|
|
1945
2188
|
"""Walk through the component tree and memoize all stateful components.
|
|
@@ -16,7 +16,7 @@ from .responsive import (
|
|
|
16
16
|
tablet_only,
|
|
17
17
|
)
|
|
18
18
|
from .upload import (
|
|
19
|
-
|
|
19
|
+
UploadNamespace,
|
|
20
20
|
cancel_upload,
|
|
21
21
|
clear_selected_files,
|
|
22
22
|
get_upload_dir,
|
|
@@ -31,4 +31,4 @@ debounce_input = DebounceInput.create
|
|
|
31
31
|
foreach = Foreach.create
|
|
32
32
|
html = Html.create
|
|
33
33
|
match = Match.create
|
|
34
|
-
upload =
|
|
34
|
+
upload = UploadNamespace()
|
reflex/components/core/cond.py
CHANGED
|
@@ -102,15 +102,6 @@ class Cond(MemoizationLeaf):
|
|
|
102
102
|
_IS_TRUE_IMPORT,
|
|
103
103
|
)
|
|
104
104
|
|
|
105
|
-
def _apply_theme(self, theme: Component):
|
|
106
|
-
"""Apply the theme to this component.
|
|
107
|
-
|
|
108
|
-
Args:
|
|
109
|
-
theme: The theme to apply.
|
|
110
|
-
"""
|
|
111
|
-
self.comp1.apply_theme(theme) # type: ignore
|
|
112
|
-
self.comp2.apply_theme(theme) # type: ignore
|
|
113
|
-
|
|
114
105
|
|
|
115
106
|
@overload
|
|
116
107
|
def cond(condition: Any, c1: Component, c2: Any) -> Component:
|
|
@@ -172,7 +163,7 @@ def cond(condition: Any, c1: Any, c2: Any = None):
|
|
|
172
163
|
def create_var(cond_part):
|
|
173
164
|
return Var.create_safe(
|
|
174
165
|
cond_part,
|
|
175
|
-
_var_is_string=
|
|
166
|
+
_var_is_string=isinstance(cond_part, (str, Color)),
|
|
176
167
|
)
|
|
177
168
|
|
|
178
169
|
# convert the truth and false cond parts into vars so the _var_data can be obtained.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""Wrapper around react-debounce-input."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
|
-
from typing import Any, Type
|
|
5
|
+
from typing import Any, Type, Union
|
|
5
6
|
|
|
6
7
|
from reflex.components.component import Component
|
|
7
8
|
from reflex.constants import EventTriggers
|
|
@@ -34,7 +35,7 @@ class DebounceInput(Component):
|
|
|
34
35
|
force_notify_on_blur: Var[bool]
|
|
35
36
|
|
|
36
37
|
# If provided, create a fully-controlled input
|
|
37
|
-
value: Var[str]
|
|
38
|
+
value: Var[Union[str, int, float]]
|
|
38
39
|
|
|
39
40
|
# The ref to attach to the created input
|
|
40
41
|
input_ref: Var[str]
|
|
@@ -120,6 +121,8 @@ class DebounceInput(Component):
|
|
|
120
121
|
component = super().create(**props)
|
|
121
122
|
component._get_style = child._get_style
|
|
122
123
|
component.event_triggers.update(child.event_triggers)
|
|
124
|
+
component.children = child.children
|
|
125
|
+
component._rename_props = child._rename_props
|
|
123
126
|
return component
|
|
124
127
|
|
|
125
128
|
def get_event_triggers(self) -> dict[str, Any]:
|
|
@@ -7,7 +7,7 @@ from typing import Any, Dict, Literal, Optional, Union, overload
|
|
|
7
7
|
from reflex.vars import Var, BaseVar, ComputedVar
|
|
8
8
|
from reflex.event import EventChain, EventHandler, EventSpec
|
|
9
9
|
from reflex.style import Style
|
|
10
|
-
from typing import Any, Type
|
|
10
|
+
from typing import Any, Type, Union
|
|
11
11
|
from reflex.components.component import Component
|
|
12
12
|
from reflex.constants import EventTriggers
|
|
13
13
|
from reflex.vars import Var, VarData
|
|
@@ -24,7 +24,9 @@ class DebounceInput(Component):
|
|
|
24
24
|
debounce_timeout: Optional[Union[Var[int], int]] = None,
|
|
25
25
|
force_notify_by_enter: Optional[Union[Var[bool], bool]] = None,
|
|
26
26
|
force_notify_on_blur: Optional[Union[Var[bool], bool]] = None,
|
|
27
|
-
value: Optional[
|
|
27
|
+
value: Optional[
|
|
28
|
+
Union[Var[Union[str, int, float]], Union[str, int, float]]
|
|
29
|
+
] = None,
|
|
28
30
|
input_ref: Optional[Union[Var[str], str]] = None,
|
|
29
31
|
element: Optional[Union[Var[Type[Component]], Type[Component]]] = None,
|
|
30
32
|
style: Optional[Style] = None,
|