reflex 0.8.7a1__py3-none-any.whl → 0.8.8__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/web/utils/state.js +5 -5
- reflex/app.py +15 -7
- reflex/app_mixins/lifespan.py +8 -2
- reflex/compiler/compiler.py +14 -14
- reflex/compiler/templates.py +629 -102
- reflex/compiler/utils.py +30 -21
- reflex/components/base/bare.py +17 -0
- reflex/components/component.py +38 -34
- reflex/components/core/cond.py +6 -12
- reflex/components/core/foreach.py +1 -1
- reflex/components/core/match.py +83 -60
- reflex/components/dynamic.py +3 -3
- reflex/components/el/elements/forms.py +31 -14
- reflex/components/el/elements/forms.pyi +0 -5
- reflex/components/lucide/icon.py +2 -1
- reflex/components/lucide/icon.pyi +2 -1
- reflex/components/markdown/markdown.py +2 -2
- reflex/components/radix/primitives/accordion.py +1 -1
- reflex/components/radix/primitives/drawer.py +10 -22
- reflex/components/radix/primitives/drawer.pyi +4 -0
- reflex/components/radix/primitives/form.py +1 -1
- reflex/components/radix/primitives/slider.py +1 -1
- reflex/components/tags/cond_tag.py +14 -5
- reflex/components/tags/iter_tag.py +0 -26
- reflex/components/tags/match_tag.py +15 -6
- reflex/components/tags/tag.py +3 -6
- reflex/components/tags/tagless.py +14 -0
- reflex/constants/base.py +0 -2
- reflex/constants/compiler.py +1 -1
- reflex/constants/installer.py +4 -4
- reflex/custom_components/custom_components.py +202 -15
- reflex/event.py +1 -1
- reflex/experimental/client_state.py +1 -1
- reflex/istate/manager.py +2 -1
- reflex/plugins/shared_tailwind.py +87 -62
- reflex/plugins/tailwind_v3.py +2 -2
- reflex/plugins/tailwind_v4.py +4 -4
- reflex/state.py +5 -1
- reflex/utils/format.py +3 -4
- reflex/utils/frontend_skeleton.py +2 -2
- reflex/utils/imports.py +18 -0
- reflex/utils/pyi_generator.py +10 -2
- reflex/utils/telemetry.py +4 -1
- reflex/utils/templates.py +1 -6
- {reflex-0.8.7a1.dist-info → reflex-0.8.8.dist-info}/METADATA +3 -4
- {reflex-0.8.7a1.dist-info → reflex-0.8.8.dist-info}/RECORD +49 -71
- reflex/.templates/jinja/app/rxconfig.py.jinja2 +0 -9
- reflex/.templates/jinja/custom_components/README.md.jinja2 +0 -9
- reflex/.templates/jinja/custom_components/__init__.py.jinja2 +0 -1
- reflex/.templates/jinja/custom_components/demo_app.py.jinja2 +0 -39
- reflex/.templates/jinja/custom_components/pyproject.toml.jinja2 +0 -25
- reflex/.templates/jinja/custom_components/src.py.jinja2 +0 -57
- reflex/.templates/jinja/web/package.json.jinja2 +0 -27
- reflex/.templates/jinja/web/pages/_app.js.jinja2 +0 -62
- reflex/.templates/jinja/web/pages/_document.js.jinja2 +0 -9
- reflex/.templates/jinja/web/pages/base_page.js.jinja2 +0 -21
- reflex/.templates/jinja/web/pages/component.js.jinja2 +0 -2
- reflex/.templates/jinja/web/pages/custom_component.js.jinja2 +0 -22
- reflex/.templates/jinja/web/pages/index.js.jinja2 +0 -18
- reflex/.templates/jinja/web/pages/macros.js.jinja2 +0 -38
- reflex/.templates/jinja/web/pages/stateful_component.js.jinja2 +0 -15
- reflex/.templates/jinja/web/pages/stateful_components.js.jinja2 +0 -5
- reflex/.templates/jinja/web/pages/utils.js.jinja2 +0 -93
- reflex/.templates/jinja/web/styles/styles.css.jinja2 +0 -6
- reflex/.templates/jinja/web/utils/context.js.jinja2 +0 -129
- reflex/.templates/jinja/web/utils/theme.js.jinja2 +0 -1
- reflex/.templates/jinja/web/vite.config.js.jinja2 +0 -74
- reflex/components/core/client_side_routing.pyi +0 -68
- {reflex-0.8.7a1.dist-info → reflex-0.8.8.dist-info}/WHEEL +0 -0
- {reflex-0.8.7a1.dist-info → reflex-0.8.8.dist-info}/entry_points.txt +0 -0
- {reflex-0.8.7a1.dist-info → reflex-0.8.8.dist-info}/licenses/LICENSE +0 -0
|
@@ -18,6 +18,202 @@ from reflex.constants import CustomComponents
|
|
|
18
18
|
from reflex.utils import console, frontend_skeleton
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
def _pyproject_toml_template(
|
|
22
|
+
package_name: str, module_name: str, reflex_version: str
|
|
23
|
+
) -> str:
|
|
24
|
+
"""Template for custom components pyproject.toml.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
package_name: The name of the package.
|
|
28
|
+
module_name: The name of the module.
|
|
29
|
+
reflex_version: The version of Reflex.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Rendered pyproject.toml content as string.
|
|
33
|
+
"""
|
|
34
|
+
return f"""[build-system]
|
|
35
|
+
requires = ["setuptools", "wheel"]
|
|
36
|
+
build-backend = "setuptools.build_meta"
|
|
37
|
+
|
|
38
|
+
[project]
|
|
39
|
+
name = "{package_name}"
|
|
40
|
+
version = "0.0.1"
|
|
41
|
+
description = "Reflex custom component {module_name}"
|
|
42
|
+
readme = "README.md"
|
|
43
|
+
license = {{ text = "Apache-2.0" }}
|
|
44
|
+
requires-python = ">=3.10"
|
|
45
|
+
authors = [{{ name = "", email = "YOUREMAIL@domain.com" }}]
|
|
46
|
+
keywords = ["reflex","reflex-custom-components"]
|
|
47
|
+
|
|
48
|
+
dependencies = ["reflex>={reflex_version}"]
|
|
49
|
+
|
|
50
|
+
classifiers = ["Development Status :: 4 - Beta"]
|
|
51
|
+
|
|
52
|
+
[project.urls]
|
|
53
|
+
|
|
54
|
+
[project.optional-dependencies]
|
|
55
|
+
dev = ["build", "twine"]
|
|
56
|
+
|
|
57
|
+
[tool.setuptools.packages.find]
|
|
58
|
+
where = ["custom_components"]
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _readme_template(module_name: str, package_name: str) -> str:
|
|
63
|
+
"""Template for custom components README.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
module_name: The name of the module.
|
|
67
|
+
package_name: The name of the package.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
Rendered README.md content as string.
|
|
71
|
+
"""
|
|
72
|
+
return f"""# {module_name}
|
|
73
|
+
|
|
74
|
+
A Reflex custom component {module_name}.
|
|
75
|
+
|
|
76
|
+
## Installation
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install {package_name}
|
|
80
|
+
```
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _source_template(component_class_name: str, module_name: str) -> str:
|
|
85
|
+
"""Template for custom components source.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
component_class_name: The name of the component class.
|
|
89
|
+
module_name: The name of the module.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Rendered custom component source code as string.
|
|
93
|
+
"""
|
|
94
|
+
return rf'''
|
|
95
|
+
"""Reflex custom component {component_class_name}."""
|
|
96
|
+
|
|
97
|
+
# For wrapping react guide, visit https://reflex.dev/docs/wrapping-react/overview/
|
|
98
|
+
|
|
99
|
+
import reflex as rx
|
|
100
|
+
|
|
101
|
+
# Some libraries you want to wrap may require dynamic imports.
|
|
102
|
+
# This is because they they may not be compatible with Server-Side Rendering (SSR).
|
|
103
|
+
# To handle this in Reflex, all you need to do is subclass `NoSSRComponent` instead.
|
|
104
|
+
# For example:
|
|
105
|
+
# from reflex.components.component import NoSSRComponent
|
|
106
|
+
# class {component_class_name}(NoSSRComponent):
|
|
107
|
+
# pass
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class {component_class_name}(rx.Component):
|
|
111
|
+
"""{component_class_name} component."""
|
|
112
|
+
|
|
113
|
+
# The React library to wrap.
|
|
114
|
+
library = "Fill-Me"
|
|
115
|
+
|
|
116
|
+
# The React component tag.
|
|
117
|
+
tag = "Fill-Me"
|
|
118
|
+
|
|
119
|
+
# If the tag is the default export from the module, you must set is_default = True.
|
|
120
|
+
# This is normally used when components don't have curly braces around them when importing.
|
|
121
|
+
# is_default = True
|
|
122
|
+
|
|
123
|
+
# If you are wrapping another components with the same tag as a component in your project
|
|
124
|
+
# you can use aliases to differentiate between them and avoid naming conflicts.
|
|
125
|
+
# alias = "Other{component_class_name}"
|
|
126
|
+
|
|
127
|
+
# The props of the React component.
|
|
128
|
+
# Note: when Reflex compiles the component to Javascript,
|
|
129
|
+
# `snake_case` property names are automatically formatted as `camelCase`.
|
|
130
|
+
# The prop names may be defined in `camelCase` as well.
|
|
131
|
+
# some_prop: rx.Var[str] = "some default value"
|
|
132
|
+
# some_other_prop: rx.Var[int] = 1
|
|
133
|
+
|
|
134
|
+
# By default Reflex will install the library you have specified in the library property.
|
|
135
|
+
# However, sometimes you may need to install other libraries to use a component.
|
|
136
|
+
# In this case you can use the lib_dependencies property to specify other libraries to install.
|
|
137
|
+
# lib_dependencies: list[str] = []
|
|
138
|
+
|
|
139
|
+
# Event triggers declaration if any.
|
|
140
|
+
# Below is equivalent to merging `{{ "on_change": lambda e: [e] }}`
|
|
141
|
+
# onto the default event triggers of parent/base Component.
|
|
142
|
+
# The function defined for the `on_change` trigger maps event for the javascript
|
|
143
|
+
# trigger to what will be passed to the backend event handler function.
|
|
144
|
+
# on_change: rx.EventHandler[lambda e: [e]]
|
|
145
|
+
|
|
146
|
+
# To add custom code to your component
|
|
147
|
+
# def _get_custom_code(self) -> str:
|
|
148
|
+
# return "const customCode = 'customCode';"
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
{module_name} = {component_class_name}.create
|
|
152
|
+
'''
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _init_template(module_name: str) -> str:
|
|
156
|
+
"""Template for custom components __init__.py.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
module_name: The name of the module.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
Rendered __init__.py content as string.
|
|
163
|
+
"""
|
|
164
|
+
return f"from .{module_name} import *"
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _demo_app_template(custom_component_module_dir: str, module_name: str) -> str:
|
|
168
|
+
"""Template for custom components demo app.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
custom_component_module_dir: The directory of the custom component module.
|
|
172
|
+
module_name: The name of the module.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
Rendered demo app source code as string.
|
|
176
|
+
"""
|
|
177
|
+
return rf'''
|
|
178
|
+
"""Welcome to Reflex! This file showcases the custom component in a basic app."""
|
|
179
|
+
|
|
180
|
+
from rxconfig import config
|
|
181
|
+
|
|
182
|
+
import reflex as rx
|
|
183
|
+
|
|
184
|
+
from {custom_component_module_dir} import {module_name}
|
|
185
|
+
|
|
186
|
+
filename = f"{{config.app_name}}/{{config.app_name}}.py"
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class State(rx.State):
|
|
190
|
+
"""The app state."""
|
|
191
|
+
pass
|
|
192
|
+
|
|
193
|
+
def index() -> rx.Component:
|
|
194
|
+
return rx.center(
|
|
195
|
+
rx.theme_panel(),
|
|
196
|
+
rx.vstack(
|
|
197
|
+
rx.heading("Welcome to Reflex!", size="9"),
|
|
198
|
+
rx.text(
|
|
199
|
+
"Test your custom component by editing ",
|
|
200
|
+
rx.code(filename),
|
|
201
|
+
font_size="2em",
|
|
202
|
+
),
|
|
203
|
+
{module_name}(),
|
|
204
|
+
align="center",
|
|
205
|
+
spacing="7",
|
|
206
|
+
),
|
|
207
|
+
height="100vh",
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# Add state and page to the app.
|
|
212
|
+
app = rx.App()
|
|
213
|
+
app.add_page(index)
|
|
214
|
+
'''
|
|
215
|
+
|
|
216
|
+
|
|
21
217
|
def set_loglevel(ctx: Any, self: Any, value: str | None):
|
|
22
218
|
"""Set the log level.
|
|
23
219
|
|
|
@@ -77,11 +273,9 @@ def _create_package_config(module_name: str, package_name: str):
|
|
|
77
273
|
module_name: The name of the module.
|
|
78
274
|
package_name: The name of the package typically constructed with `reflex-` prefix and a meaningful library name.
|
|
79
275
|
"""
|
|
80
|
-
from reflex.compiler import templates
|
|
81
|
-
|
|
82
276
|
pyproject = Path(CustomComponents.PYPROJECT_TOML)
|
|
83
277
|
pyproject.write_text(
|
|
84
|
-
|
|
278
|
+
_pyproject_toml_template(
|
|
85
279
|
module_name=module_name,
|
|
86
280
|
package_name=package_name,
|
|
87
281
|
reflex_version=constants.Reflex.VERSION,
|
|
@@ -96,11 +290,9 @@ def _create_readme(module_name: str, package_name: str):
|
|
|
96
290
|
module_name: The name of the module.
|
|
97
291
|
package_name: The name of the python package to be published.
|
|
98
292
|
"""
|
|
99
|
-
from reflex.compiler import templates
|
|
100
|
-
|
|
101
293
|
readme = Path(CustomComponents.PACKAGE_README)
|
|
102
294
|
readme.write_text(
|
|
103
|
-
|
|
295
|
+
_readme_template(
|
|
104
296
|
module_name=module_name,
|
|
105
297
|
package_name=package_name,
|
|
106
298
|
)
|
|
@@ -119,19 +311,15 @@ def _write_source_and_init_py(
|
|
|
119
311
|
component_class_name: The name of the component class.
|
|
120
312
|
module_name: The name of the module.
|
|
121
313
|
"""
|
|
122
|
-
from reflex.compiler import templates
|
|
123
|
-
|
|
124
314
|
module_path = custom_component_src_dir / f"{module_name}.py"
|
|
125
315
|
module_path.write_text(
|
|
126
|
-
|
|
316
|
+
_source_template(
|
|
127
317
|
component_class_name=component_class_name, module_name=module_name
|
|
128
318
|
)
|
|
129
319
|
)
|
|
130
320
|
|
|
131
321
|
init_path = custom_component_src_dir / CustomComponents.INIT_FILE
|
|
132
|
-
init_path.write_text(
|
|
133
|
-
templates.CUSTOM_COMPONENTS_INIT_FILE.render(module_name=module_name)
|
|
134
|
-
)
|
|
322
|
+
init_path.write_text(_init_template(module_name=module_name))
|
|
135
323
|
|
|
136
324
|
|
|
137
325
|
def _populate_demo_app(name_variants: NameVariants):
|
|
@@ -141,7 +329,6 @@ def _populate_demo_app(name_variants: NameVariants):
|
|
|
141
329
|
name_variants: the tuple including various names such as package name, class name needed for the project.
|
|
142
330
|
"""
|
|
143
331
|
from reflex import constants
|
|
144
|
-
from reflex.compiler import templates
|
|
145
332
|
from reflex.reflex import _init
|
|
146
333
|
|
|
147
334
|
demo_app_dir = Path(name_variants.demo_app_dir)
|
|
@@ -155,10 +342,10 @@ def _populate_demo_app(name_variants: NameVariants):
|
|
|
155
342
|
# We start with the blank template as basis.
|
|
156
343
|
_init(name=demo_app_name, template=constants.Templates.DEFAULT)
|
|
157
344
|
# Then overwrite the app source file with the one we want for testing custom components.
|
|
158
|
-
# This source file is rendered using
|
|
345
|
+
# This source file is rendered using template file.
|
|
159
346
|
demo_file = Path(f"{demo_app_name}/{demo_app_name}.py")
|
|
160
347
|
demo_file.write_text(
|
|
161
|
-
|
|
348
|
+
_demo_app_template(
|
|
162
349
|
custom_component_module_dir=name_variants.custom_component_module_dir,
|
|
163
350
|
module_name=name_variants.module_name,
|
|
164
351
|
)
|
reflex/event.py
CHANGED
|
@@ -1930,7 +1930,7 @@ class LiteralEventVar(VarOperationCall, LiteralVar, EventVar):
|
|
|
1930
1930
|
_var_type=EventSpec,
|
|
1931
1931
|
_var_data=_var_data,
|
|
1932
1932
|
_var_value=value,
|
|
1933
|
-
_func=FunctionStringVar("
|
|
1933
|
+
_func=FunctionStringVar("ReflexEvent"),
|
|
1934
1934
|
_args=(
|
|
1935
1935
|
# event handler name
|
|
1936
1936
|
".".join(
|
reflex/istate/manager.py
CHANGED
|
@@ -667,7 +667,8 @@ class StateManagerRedis(StateManager):
|
|
|
667
667
|
_substate_key(client_token, substate),
|
|
668
668
|
substate,
|
|
669
669
|
lock_id,
|
|
670
|
-
)
|
|
670
|
+
),
|
|
671
|
+
name=f"reflex_set_state|{client_token}|{substate.get_full_name()}",
|
|
671
672
|
)
|
|
672
673
|
for substate in state.substates.values()
|
|
673
674
|
]
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
"""Tailwind CSS configuration types for Reflex plugins."""
|
|
2
2
|
|
|
3
3
|
import dataclasses
|
|
4
|
+
from collections.abc import Mapping
|
|
4
5
|
from copy import deepcopy
|
|
5
6
|
from typing import Any, Literal, TypedDict
|
|
6
7
|
|
|
7
|
-
from typing_extensions import NotRequired
|
|
8
|
-
|
|
9
|
-
from reflex.utils.decorator import once
|
|
8
|
+
from typing_extensions import NotRequired, Unpack
|
|
10
9
|
|
|
11
10
|
from .base import Plugin as PluginBase
|
|
12
11
|
|
|
@@ -80,71 +79,97 @@ class TailwindConfig(TypedDict):
|
|
|
80
79
|
plugins: NotRequired[list[TailwindPluginConfig]]
|
|
81
80
|
|
|
82
81
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
def tailwind_config_js_template(
|
|
83
|
+
*, default_content: list[str], **kwargs: Unpack[TailwindConfig]
|
|
84
|
+
):
|
|
85
|
+
"""Generate a Tailwind CSS configuration file in JavaScript format.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
default_content: The default content to use if none is provided.
|
|
89
|
+
**kwargs: The template variables.
|
|
86
90
|
|
|
87
91
|
Returns:
|
|
88
92
|
The Tailwind config template.
|
|
89
93
|
"""
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
import plugin{{ loop.index }} from {{ plugin | tojson }};
|
|
109
|
-
{%- endif %}
|
|
110
|
-
{%- endfor %}
|
|
111
|
-
|
|
112
|
-
{%- for preset in presets %}
|
|
113
|
-
import preset{{ loop.index }} from {{ preset | tojson }};
|
|
114
|
-
{%- endfor %}
|
|
115
|
-
|
|
116
|
-
export default {
|
|
117
|
-
content: {{ (content if content is defined else DEFAULT_CONTENT) | tojson }},
|
|
118
|
-
{% if theme is defined %}theme: {{ theme | tojson }},{% else %}theme: {},{% endif %}
|
|
119
|
-
{% if darkMode is defined %}darkMode: {{ darkMode | tojson }},{% endif %}
|
|
120
|
-
{% if corePlugins is defined %}corePlugins: {{ corePlugins | tojson }},{% endif %}
|
|
121
|
-
{% if important is defined %}important: {{ important | tojson }},{% endif %}
|
|
122
|
-
{% if prefix is defined %}prefix: {{ prefix | tojson }},{% endif %}
|
|
123
|
-
{% if separator is defined %}separator: {{ separator | tojson }},{% endif %}
|
|
124
|
-
{% if presets is defined %}
|
|
125
|
-
presets: [
|
|
126
|
-
{% for preset in presets %}
|
|
127
|
-
preset{{ loop.index }},
|
|
128
|
-
{% endfor %}
|
|
129
|
-
],
|
|
130
|
-
{% endif %}
|
|
131
|
-
plugins: [
|
|
132
|
-
{% for plugin in plugins %}
|
|
133
|
-
{% if plugin is mapping and plugin.call is defined %}
|
|
134
|
-
{{ plugin.call }}(
|
|
135
|
-
{%- if plugin.args is defined -%}
|
|
136
|
-
{{ plugin.args | tojson }}
|
|
137
|
-
{%- endif -%}
|
|
138
|
-
),
|
|
139
|
-
{% else %}
|
|
140
|
-
plugin{{ loop.index }},
|
|
141
|
-
{% endif %}
|
|
142
|
-
{% endfor %}
|
|
94
|
+
import json
|
|
95
|
+
|
|
96
|
+
# Extract parameters
|
|
97
|
+
plugins = kwargs.get("plugins", [])
|
|
98
|
+
presets = kwargs.get("presets", [])
|
|
99
|
+
content = kwargs.get("content")
|
|
100
|
+
theme = kwargs.get("theme")
|
|
101
|
+
dark_mode = kwargs.get("darkMode")
|
|
102
|
+
core_plugins = kwargs.get("corePlugins")
|
|
103
|
+
important = kwargs.get("important")
|
|
104
|
+
prefix = kwargs.get("prefix")
|
|
105
|
+
separator = kwargs.get("separator")
|
|
106
|
+
|
|
107
|
+
# Extract destructured imports from plugin dicts only
|
|
108
|
+
imports = [
|
|
109
|
+
plugin["import"]
|
|
110
|
+
for plugin in plugins
|
|
111
|
+
if isinstance(plugin, Mapping) and "import" in plugin
|
|
143
112
|
]
|
|
144
|
-
};
|
|
145
|
-
"""
|
|
146
113
|
|
|
147
|
-
|
|
114
|
+
# Generate import statements for destructured imports
|
|
115
|
+
import_lines = "\n".join(
|
|
116
|
+
[
|
|
117
|
+
f"import {{ {imp['name']} }} from {json.dumps(imp['from'])};"
|
|
118
|
+
for imp in imports
|
|
119
|
+
]
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Generate plugin imports
|
|
123
|
+
plugin_imports = []
|
|
124
|
+
for i, plugin in enumerate(plugins, 1):
|
|
125
|
+
if isinstance(plugin, Mapping) and "call" not in plugin:
|
|
126
|
+
plugin_imports.append(
|
|
127
|
+
f"import plugin{i} from {json.dumps(plugin['name'])};"
|
|
128
|
+
)
|
|
129
|
+
elif not isinstance(plugin, Mapping):
|
|
130
|
+
plugin_imports.append(f"import plugin{i} from {json.dumps(plugin)};")
|
|
131
|
+
|
|
132
|
+
plugin_imports_lines = "\n".join(plugin_imports)
|
|
133
|
+
|
|
134
|
+
presets_imports_lines = "\n".join(
|
|
135
|
+
[
|
|
136
|
+
f"import preset{i} from {json.dumps(preset)};"
|
|
137
|
+
for i, preset in enumerate(presets, 1)
|
|
138
|
+
]
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Generate plugin array
|
|
142
|
+
plugin_list = []
|
|
143
|
+
for i, plugin in enumerate(plugins, 1):
|
|
144
|
+
if isinstance(plugin, Mapping) and "call" in plugin:
|
|
145
|
+
args_part = ""
|
|
146
|
+
if "args" in plugin:
|
|
147
|
+
args_part = json.dumps(plugin["args"])
|
|
148
|
+
plugin_list.append(f"{plugin['call']}({args_part})")
|
|
149
|
+
else:
|
|
150
|
+
plugin_list.append(f"plugin{i}")
|
|
151
|
+
|
|
152
|
+
plugin_use_str = ",".join(plugin_list)
|
|
153
|
+
|
|
154
|
+
return rf"""
|
|
155
|
+
{import_lines}
|
|
156
|
+
|
|
157
|
+
{plugin_imports_lines}
|
|
158
|
+
|
|
159
|
+
{presets_imports_lines}
|
|
160
|
+
|
|
161
|
+
export default {{
|
|
162
|
+
content: {json.dumps(content if content else default_content)},
|
|
163
|
+
theme: {json.dumps(theme if theme else {})},
|
|
164
|
+
{f"darkMode: {json.dumps(dark_mode)}," if dark_mode is not None else ""}
|
|
165
|
+
{f"corePlugins: {json.dumps(core_plugins)}," if core_plugins is not None else ""}
|
|
166
|
+
{f"importants: {json.dumps(important)}," if important is not None else ""}
|
|
167
|
+
{f"prefix: {json.dumps(prefix)}," if prefix is not None else ""}
|
|
168
|
+
{f"separator: {json.dumps(separator)}," if separator is not None else ""}
|
|
169
|
+
{f"presets: [{', '.join(f'preset{i}' for i in range(1, len(presets) + 1))}]," if presets else ""}
|
|
170
|
+
plugins: [{plugin_use_str}]
|
|
171
|
+
}};
|
|
172
|
+
"""
|
|
148
173
|
|
|
149
174
|
|
|
150
175
|
@dataclasses.dataclass
|
reflex/plugins/tailwind_v3.py
CHANGED
|
@@ -48,9 +48,9 @@ def compile_config(config: TailwindConfig):
|
|
|
48
48
|
Returns:
|
|
49
49
|
The compiled Tailwind config.
|
|
50
50
|
"""
|
|
51
|
-
return Constants.CONFIG, tailwind_config_js_template(
|
|
51
|
+
return Constants.CONFIG, tailwind_config_js_template(
|
|
52
52
|
**config,
|
|
53
|
-
|
|
53
|
+
default_content=Constants.CONTENT,
|
|
54
54
|
)
|
|
55
55
|
|
|
56
56
|
|
reflex/plugins/tailwind_v4.py
CHANGED
|
@@ -17,7 +17,7 @@ class Constants(SimpleNamespace):
|
|
|
17
17
|
"""Tailwind constants."""
|
|
18
18
|
|
|
19
19
|
# The Tailwindcss version
|
|
20
|
-
VERSION = "tailwindcss@4.1.
|
|
20
|
+
VERSION = "tailwindcss@4.1.12"
|
|
21
21
|
# The Tailwind config.
|
|
22
22
|
CONFIG = "tailwind.config.js"
|
|
23
23
|
# Default Tailwind content paths
|
|
@@ -47,9 +47,9 @@ def compile_config(config: TailwindConfig):
|
|
|
47
47
|
Returns:
|
|
48
48
|
The compiled Tailwind config.
|
|
49
49
|
"""
|
|
50
|
-
return Constants.CONFIG, tailwind_config_js_template(
|
|
50
|
+
return Constants.CONFIG, tailwind_config_js_template(
|
|
51
51
|
**config,
|
|
52
|
-
|
|
52
|
+
default_content=Constants.CONTENT,
|
|
53
53
|
)
|
|
54
54
|
|
|
55
55
|
|
|
@@ -156,7 +156,7 @@ class TailwindV4Plugin(TailwindPlugin):
|
|
|
156
156
|
return [
|
|
157
157
|
*super().get_frontend_development_dependencies(**context),
|
|
158
158
|
Constants.VERSION,
|
|
159
|
-
"@tailwindcss/postcss@4.1.
|
|
159
|
+
"@tailwindcss/postcss@4.1.12",
|
|
160
160
|
]
|
|
161
161
|
|
|
162
162
|
def pre_compile(self, **context):
|
reflex/state.py
CHANGED
|
@@ -11,6 +11,7 @@ import functools
|
|
|
11
11
|
import inspect
|
|
12
12
|
import pickle
|
|
13
13
|
import sys
|
|
14
|
+
import time
|
|
14
15
|
import typing
|
|
15
16
|
import warnings
|
|
16
17
|
from collections.abc import AsyncIterator, Callable, Sequence
|
|
@@ -284,7 +285,10 @@ async def _resolve_delta(delta: Delta) -> Delta:
|
|
|
284
285
|
for state_name, state_delta in delta.items():
|
|
285
286
|
for var_name, value in state_delta.items():
|
|
286
287
|
if asyncio.iscoroutine(value):
|
|
287
|
-
tasks[state_name, var_name] = asyncio.create_task(
|
|
288
|
+
tasks[state_name, var_name] = asyncio.create_task(
|
|
289
|
+
value,
|
|
290
|
+
name=f"reflex_resolve_delta|{state_name}|{var_name}|{time.time()}",
|
|
291
|
+
)
|
|
288
292
|
for (state_name, var_name), task in tasks.items():
|
|
289
293
|
delta[state_name][var_name] = await task
|
|
290
294
|
return delta
|
reflex/utils/format.py
CHANGED
|
@@ -330,7 +330,7 @@ def format_route(route: str) -> str:
|
|
|
330
330
|
|
|
331
331
|
def format_match(
|
|
332
332
|
cond: str | Var,
|
|
333
|
-
match_cases: list[list[Var]],
|
|
333
|
+
match_cases: list[tuple[list[Var], Var]],
|
|
334
334
|
default: Var,
|
|
335
335
|
) -> str:
|
|
336
336
|
"""Format a match expression whose return type is a Var.
|
|
@@ -347,8 +347,7 @@ def format_match(
|
|
|
347
347
|
switch_code = f"(() => {{ switch (JSON.stringify({cond})) {{"
|
|
348
348
|
|
|
349
349
|
for case in match_cases:
|
|
350
|
-
conditions = case
|
|
351
|
-
return_value = case[-1]
|
|
350
|
+
conditions, return_value = case
|
|
352
351
|
|
|
353
352
|
case_conditions = " ".join(
|
|
354
353
|
[f"case JSON.stringify({condition!s}):" for condition in conditions]
|
|
@@ -519,7 +518,7 @@ def format_event(event_spec: EventSpec) -> str:
|
|
|
519
518
|
|
|
520
519
|
if event_spec.client_handler_name:
|
|
521
520
|
event_args.append(wrap(event_spec.client_handler_name, '"'))
|
|
522
|
-
return f"
|
|
521
|
+
return f"ReflexEvent({', '.join(event_args)})"
|
|
523
522
|
|
|
524
523
|
|
|
525
524
|
if TYPE_CHECKING:
|
|
@@ -169,7 +169,7 @@ def _update_react_router_config(config: Config, prerender_routes: bool = False):
|
|
|
169
169
|
|
|
170
170
|
|
|
171
171
|
def _compile_package_json():
|
|
172
|
-
return templates.
|
|
172
|
+
return templates.package_json_template(
|
|
173
173
|
scripts={
|
|
174
174
|
"dev": constants.PackageJson.Commands.DEV,
|
|
175
175
|
"export": constants.PackageJson.Commands.EXPORT,
|
|
@@ -192,7 +192,7 @@ def _compile_vite_config(config: Config):
|
|
|
192
192
|
base = "/"
|
|
193
193
|
if frontend_path := config.frontend_path.strip("/"):
|
|
194
194
|
base += frontend_path + "/"
|
|
195
|
-
return templates.
|
|
195
|
+
return templates.vite_config_template(base=base)
|
|
196
196
|
|
|
197
197
|
|
|
198
198
|
def initialize_vite_config():
|
reflex/utils/imports.py
CHANGED
|
@@ -7,6 +7,24 @@ from collections import defaultdict
|
|
|
7
7
|
from collections.abc import Mapping, Sequence
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
def merge_parsed_imports(
|
|
11
|
+
*imports: ImmutableParsedImportDict,
|
|
12
|
+
) -> ParsedImportDict:
|
|
13
|
+
"""Merge multiple parsed import dicts together.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
*imports: The list of import dicts to merge.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
The merged import dicts.
|
|
20
|
+
"""
|
|
21
|
+
all_imports: defaultdict[str, list[ImportVar]] = defaultdict(list)
|
|
22
|
+
for import_dict in imports:
|
|
23
|
+
for lib, fields in import_dict.items():
|
|
24
|
+
all_imports[lib].extend(fields)
|
|
25
|
+
return all_imports
|
|
26
|
+
|
|
27
|
+
|
|
10
28
|
def merge_imports(
|
|
11
29
|
*imports: ImportDict | ParsedImportDict | ParsedImportTuple,
|
|
12
30
|
) -> ParsedImportDict:
|
reflex/utils/pyi_generator.py
CHANGED
|
@@ -145,7 +145,7 @@ def _get_type_hint(
|
|
|
145
145
|
res = ""
|
|
146
146
|
args = get_args(value)
|
|
147
147
|
|
|
148
|
-
if value is type(None):
|
|
148
|
+
if value is type(None) or value is None:
|
|
149
149
|
return "None"
|
|
150
150
|
|
|
151
151
|
if rx_types.is_union(value):
|
|
@@ -425,10 +425,18 @@ def type_to_ast(typ: Any, cls: type) -> ast.expr:
|
|
|
425
425
|
Returns:
|
|
426
426
|
The AST representation of the type annotation.
|
|
427
427
|
"""
|
|
428
|
-
if typ is type(None):
|
|
428
|
+
if typ is type(None) or typ is None:
|
|
429
429
|
return ast.Name(id="None")
|
|
430
430
|
|
|
431
431
|
origin = get_origin(typ)
|
|
432
|
+
if origin is typing.Literal:
|
|
433
|
+
return ast.Subscript(
|
|
434
|
+
value=ast.Name(id="Literal"),
|
|
435
|
+
slice=ast.Tuple(
|
|
436
|
+
elts=[ast.Constant(value=val) for val in get_args(typ)], ctx=ast.Load()
|
|
437
|
+
),
|
|
438
|
+
ctx=ast.Load(),
|
|
439
|
+
)
|
|
432
440
|
if origin is UnionType:
|
|
433
441
|
origin = typing.Union
|
|
434
442
|
|
reflex/utils/telemetry.py
CHANGED
|
@@ -353,7 +353,10 @@ def send(event: str, telemetry_enabled: bool | None = None, **kwargs):
|
|
|
353
353
|
|
|
354
354
|
try:
|
|
355
355
|
# Within an event loop context, send the event asynchronously.
|
|
356
|
-
task = asyncio.create_task(
|
|
356
|
+
task = asyncio.create_task(
|
|
357
|
+
async_send(event, telemetry_enabled, **kwargs),
|
|
358
|
+
name=f"reflex_send_telemetry_event|{event}",
|
|
359
|
+
)
|
|
357
360
|
background_tasks.add(task)
|
|
358
361
|
task.add_done_callback(background_tasks.discard)
|
|
359
362
|
except RuntimeError:
|
reflex/utils/templates.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""This module provides utilities for managing Reflex app templates."""
|
|
2
2
|
|
|
3
3
|
import dataclasses
|
|
4
|
-
import re
|
|
5
4
|
import shutil
|
|
6
5
|
import tempfile
|
|
7
6
|
import zipfile
|
|
@@ -33,12 +32,8 @@ def create_config(app_name: str):
|
|
|
33
32
|
# Import here to avoid circular imports.
|
|
34
33
|
from reflex.compiler import templates
|
|
35
34
|
|
|
36
|
-
config_name = f"{re.sub(r'[^a-zA-Z]', '', app_name).capitalize()}Config"
|
|
37
|
-
|
|
38
35
|
console.debug(f"Creating {constants.Config.FILE}")
|
|
39
|
-
constants.Config.FILE.write_text(
|
|
40
|
-
templates.RXCONFIG.render(app_name=app_name, config_name=config_name)
|
|
41
|
-
)
|
|
36
|
+
constants.Config.FILE.write_text(templates.rxconfig_template(app_name=app_name))
|
|
42
37
|
|
|
43
38
|
|
|
44
39
|
def initialize_app_directory(
|