reflex 0.4.6a3__py3-none-any.whl → 0.4.7__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 +1 -0
- reflex/.templates/jinja/custom_components/pyproject.toml.jinja2 +1 -1
- reflex/.templates/jinja/custom_components/src.py.jinja2 +8 -8
- reflex/.templates/jinja/web/pages/index.js.jinja2 +0 -4
- reflex/.templates/jinja/web/pages/stateful_component.js.jinja2 +2 -6
- reflex/.templates/web/utils/state.js +6 -1
- reflex/__init__.py +2 -0
- reflex/__init__.pyi +2 -0
- reflex/app.py +12 -16
- reflex/app.pyi +2 -0
- reflex/compiler/compiler.py +10 -11
- reflex/compiler/utils.py +3 -3
- reflex/components/chakra/forms/pininput.py +2 -1
- reflex/components/component.py +71 -131
- reflex/components/core/banner.py +1 -1
- reflex/components/core/upload.py +2 -1
- reflex/components/datadisplay/__init__.py +1 -0
- reflex/components/datadisplay/logo.py +49 -0
- reflex/components/el/elements/forms.py +7 -4
- reflex/components/el/elements/forms.pyi +0 -1
- reflex/components/lucide/icon.py +3 -2
- reflex/components/lucide/icon.pyi +2 -2
- reflex/components/markdown/markdown.py +10 -6
- reflex/components/markdown/markdown.pyi +0 -3
- reflex/components/radix/themes/components/select.py +10 -3
- reflex/config.py +1 -1
- reflex/config.pyi +1 -1
- reflex/constants/base.py +4 -5
- reflex/constants/base.pyi +94 -0
- reflex/constants/compiler.py +8 -0
- reflex/custom_components/custom_components.py +33 -38
- reflex/experimental/__init__.py +14 -0
- reflex/experimental/hooks.py +75 -0
- reflex/page.py +1 -1
- reflex/reflex.py +18 -32
- reflex/style.py +4 -4
- reflex/testing.py +1 -1
- reflex/utils/console.py +6 -4
- reflex/utils/exec.py +17 -1
- reflex/utils/export.py +0 -3
- reflex/utils/prerequisites.py +243 -43
- reflex/utils/processes.py +6 -1
- reflex/utils/telemetry.py +14 -2
- reflex/utils/types.py +3 -2
- reflex/vars.py +6 -6
- reflex/vars.pyi +2 -1
- {reflex-0.4.6a3.dist-info → reflex-0.4.7.dist-info}/METADATA +15 -10
- {reflex-0.4.6a3.dist-info → reflex-0.4.7.dist-info}/RECORD +51 -65
- {reflex-0.4.6a3.dist-info → reflex-0.4.7.dist-info}/WHEEL +1 -1
- reflex/.templates/apps/sidebar/README.md +0 -69
- reflex/.templates/apps/sidebar/assets/favicon.ico +0 -0
- reflex/.templates/apps/sidebar/assets/github.svg +0 -10
- reflex/.templates/apps/sidebar/assets/logo.svg +0 -68
- reflex/.templates/apps/sidebar/assets/paneleft.svg +0 -13
- reflex/.templates/apps/sidebar/assets/reflex_black.svg +0 -37
- reflex/.templates/apps/sidebar/assets/reflex_white.svg +0 -8
- reflex/.templates/apps/sidebar/code/__init__.py +0 -1
- reflex/.templates/apps/sidebar/code/components/__init__.py +0 -0
- reflex/.templates/apps/sidebar/code/components/sidebar.py +0 -152
- reflex/.templates/apps/sidebar/code/pages/__init__.py +0 -3
- reflex/.templates/apps/sidebar/code/pages/dashboard.py +0 -22
- reflex/.templates/apps/sidebar/code/pages/index.py +0 -18
- reflex/.templates/apps/sidebar/code/pages/settings.py +0 -61
- reflex/.templates/apps/sidebar/code/sidebar.py +0 -16
- reflex/.templates/apps/sidebar/code/styles.py +0 -60
- reflex/.templates/apps/sidebar/code/templates/__init__.py +0 -1
- reflex/.templates/apps/sidebar/code/templates/template.py +0 -145
- {reflex-0.4.6a3.dist-info → reflex-0.4.7.dist-info}/LICENSE +0 -0
- {reflex-0.4.6a3.dist-info → reflex-0.4.7.dist-info}/entry_points.txt +0 -0
|
@@ -221,7 +221,7 @@ RENAMED_ICONS_05 = {
|
|
|
221
221
|
"dot_square": "square_dot",
|
|
222
222
|
"download_cloud": "cloud_download",
|
|
223
223
|
"equal_square": "square_equal",
|
|
224
|
-
"form_input": "
|
|
224
|
+
"form_input": "rectangle_ellipsis",
|
|
225
225
|
"function_square": "square_function",
|
|
226
226
|
"gantt_chart_square": "square_gantt_chart",
|
|
227
227
|
"gauge_circle": "circle_gauge",
|
|
@@ -232,7 +232,7 @@ RENAMED_ICONS_05 = {
|
|
|
232
232
|
"ice_cream_2": "ice_cream_bowl",
|
|
233
233
|
"indent": "indent_increase",
|
|
234
234
|
"kanban_square": "square_kanban",
|
|
235
|
-
"kanban_square_dashed": "
|
|
235
|
+
"kanban_square_dashed": "square_dashed_kanban",
|
|
236
236
|
"laptop_2": "laptop_minimal",
|
|
237
237
|
"library_square": "square_library",
|
|
238
238
|
"loader_2": "loader_circle",
|
|
@@ -133,7 +133,7 @@ class Markdown(Component):
|
|
|
133
133
|
**props,
|
|
134
134
|
)
|
|
135
135
|
|
|
136
|
-
def
|
|
136
|
+
def _get_all_custom_components(
|
|
137
137
|
self, seen: set[str] | None = None
|
|
138
138
|
) -> set[CustomComponent]:
|
|
139
139
|
"""Get all the custom components used by the component.
|
|
@@ -144,11 +144,13 @@ class Markdown(Component):
|
|
|
144
144
|
Returns:
|
|
145
145
|
The set of custom components.
|
|
146
146
|
"""
|
|
147
|
-
custom_components = super().
|
|
147
|
+
custom_components = super()._get_all_custom_components(seen=seen)
|
|
148
148
|
|
|
149
149
|
# Get the custom components for each tag.
|
|
150
150
|
for component in self.component_map.values():
|
|
151
|
-
custom_components |= component(_MOCK_ARG).
|
|
151
|
+
custom_components |= component(_MOCK_ARG)._get_all_custom_components(
|
|
152
|
+
seen=seen
|
|
153
|
+
)
|
|
152
154
|
|
|
153
155
|
return custom_components
|
|
154
156
|
|
|
@@ -183,7 +185,9 @@ class Markdown(Component):
|
|
|
183
185
|
|
|
184
186
|
# Get the imports for each component.
|
|
185
187
|
for component in self.component_map.values():
|
|
186
|
-
imports = utils.merge_imports(
|
|
188
|
+
imports = utils.merge_imports(
|
|
189
|
+
imports, component(_MOCK_ARG)._get_all_imports()
|
|
190
|
+
)
|
|
187
191
|
|
|
188
192
|
# Get the imports for the code components.
|
|
189
193
|
imports = utils.merge_imports(
|
|
@@ -293,8 +297,8 @@ class Markdown(Component):
|
|
|
293
297
|
hooks = set()
|
|
294
298
|
for _component in self.component_map.values():
|
|
295
299
|
comp = _component(_MOCK_ARG)
|
|
296
|
-
hooks
|
|
297
|
-
hooks
|
|
300
|
+
hooks.update(comp._get_all_hooks_internal())
|
|
301
|
+
hooks.update(comp._get_all_hooks())
|
|
298
302
|
formatted_hooks = "\n".join(hooks)
|
|
299
303
|
return f"""
|
|
300
304
|
function {self._get_component_map_name()} () {{
|
|
@@ -123,9 +123,6 @@ class Markdown(Component):
|
|
|
123
123
|
The markdown component.
|
|
124
124
|
"""
|
|
125
125
|
...
|
|
126
|
-
def get_custom_components(
|
|
127
|
-
self, seen: set[str] | None = None
|
|
128
|
-
) -> set[CustomComponent]: ...
|
|
129
126
|
def get_component(self, tag: str, **props) -> Component: ...
|
|
130
127
|
def format_component(self, tag: str, **props) -> str: ...
|
|
131
128
|
def format_component_map(self) -> dict[str, str]: ...
|
|
@@ -198,6 +198,15 @@ class HighLevelSelect(SelectRoot):
|
|
|
198
198
|
Returns:
|
|
199
199
|
The select component.
|
|
200
200
|
"""
|
|
201
|
+
trigger_prop_list = [
|
|
202
|
+
"placeholder",
|
|
203
|
+
"variant",
|
|
204
|
+
"radius",
|
|
205
|
+
"width",
|
|
206
|
+
"flex_shrink",
|
|
207
|
+
"custom_attrs",
|
|
208
|
+
]
|
|
209
|
+
|
|
201
210
|
content_props = {
|
|
202
211
|
prop: props.pop(prop)
|
|
203
212
|
for prop in ["high_contrast", "position"]
|
|
@@ -205,9 +214,7 @@ class HighLevelSelect(SelectRoot):
|
|
|
205
214
|
}
|
|
206
215
|
|
|
207
216
|
trigger_props = {
|
|
208
|
-
prop: props.pop(prop)
|
|
209
|
-
for prop in ["placeholder", "variant", "radius", "width", "flex_shrink"]
|
|
210
|
-
if prop in props
|
|
217
|
+
prop: props.pop(prop) for prop in trigger_prop_list if prop in props
|
|
211
218
|
}
|
|
212
219
|
|
|
213
220
|
color_scheme = props.pop("color_scheme", None)
|
reflex/config.py
CHANGED
reflex/config.pyi
CHANGED
|
@@ -104,7 +104,7 @@ class Config(Base):
|
|
|
104
104
|
@staticmethod
|
|
105
105
|
def check_deprecated_values(**kwargs) -> None: ...
|
|
106
106
|
def update_from_env(self) -> None: ...
|
|
107
|
-
def get_event_namespace(self) -> str
|
|
107
|
+
def get_event_namespace(self) -> str: ...
|
|
108
108
|
def _set_persistent(self, **kwargs) -> None: ...
|
|
109
109
|
|
|
110
110
|
def get_config(reload: bool = ...) -> Config: ...
|
reflex/constants/base.py
CHANGED
|
@@ -88,12 +88,11 @@ class ReflexHostingCLI(SimpleNamespace):
|
|
|
88
88
|
class Templates(SimpleNamespace):
|
|
89
89
|
"""Constants related to Templates."""
|
|
90
90
|
|
|
91
|
-
#
|
|
92
|
-
|
|
93
|
-
template_dirs = next(os.walk(template_dir))[1]
|
|
91
|
+
# The route on Reflex backend to query which templates are available and their URLs.
|
|
92
|
+
APP_TEMPLATES_ROUTE = "/app-templates"
|
|
94
93
|
|
|
95
|
-
#
|
|
96
|
-
|
|
94
|
+
# The default template
|
|
95
|
+
DEFAULT = "blank"
|
|
97
96
|
|
|
98
97
|
class Dirs(SimpleNamespace):
|
|
99
98
|
"""Folders used by the template system of Reflex."""
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Stub file for reflex/constants/base.py"""
|
|
2
|
+
# ------------------- DO NOT EDIT ----------------------
|
|
3
|
+
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
4
|
+
# ------------------------------------------------------
|
|
5
|
+
|
|
6
|
+
from typing import Any, Dict, Literal, Optional, Union, overload
|
|
7
|
+
from reflex.vars import Var, BaseVar, ComputedVar
|
|
8
|
+
from reflex.event import EventChain, EventHandler, EventSpec
|
|
9
|
+
from reflex.style import Style
|
|
10
|
+
import os
|
|
11
|
+
import platform
|
|
12
|
+
from enum import Enum
|
|
13
|
+
from importlib import metadata
|
|
14
|
+
from types import SimpleNamespace
|
|
15
|
+
from platformdirs import PlatformDirs
|
|
16
|
+
|
|
17
|
+
IS_WINDOWS = platform.system() == "Windows"
|
|
18
|
+
|
|
19
|
+
class Dirs(SimpleNamespace):
|
|
20
|
+
WEB = ".web"
|
|
21
|
+
APP_ASSETS = "assets"
|
|
22
|
+
UTILS = "utils"
|
|
23
|
+
STATIC = "_static"
|
|
24
|
+
STATE_PATH = "/".join([UTILS, "state"])
|
|
25
|
+
COMPONENTS_PATH = "/".join([UTILS, "components"])
|
|
26
|
+
CONTEXTS_PATH = "/".join([UTILS, "context"])
|
|
27
|
+
WEB_PAGES = os.path.join(WEB, "pages")
|
|
28
|
+
WEB_STATIC = os.path.join(WEB, STATIC)
|
|
29
|
+
WEB_UTILS = os.path.join(WEB, UTILS)
|
|
30
|
+
WEB_ASSETS = os.path.join(WEB, "public")
|
|
31
|
+
ENV_JSON = os.path.join(WEB, "env.json")
|
|
32
|
+
REFLEX_JSON = os.path.join(WEB, "reflex.json")
|
|
33
|
+
POSTCSS_JS = os.path.join(WEB, "postcss.config.js")
|
|
34
|
+
|
|
35
|
+
class Reflex(SimpleNamespace):
|
|
36
|
+
MODULE_NAME = "reflex"
|
|
37
|
+
VERSION = metadata.version(MODULE_NAME)
|
|
38
|
+
JSON = os.path.join(Dirs.WEB, "reflex.json")
|
|
39
|
+
_dir = os.environ.get("REFLEX_DIR", "")
|
|
40
|
+
DIR = _dir or PlatformDirs(MODULE_NAME, False).user_data_dir
|
|
41
|
+
ROOT_DIR = os.path.dirname(
|
|
42
|
+
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
class ReflexHostingCLI(SimpleNamespace):
|
|
46
|
+
MODULE_NAME = "reflex-hosting-cli"
|
|
47
|
+
|
|
48
|
+
class Templates(SimpleNamespace):
|
|
49
|
+
APP_TEMPLATES_ROUTE = "/app-templates"
|
|
50
|
+
DEFAULT = "blank"
|
|
51
|
+
|
|
52
|
+
class Dirs(SimpleNamespace):
|
|
53
|
+
BASE = os.path.join(Reflex.ROOT_DIR, Reflex.MODULE_NAME, ".templates")
|
|
54
|
+
WEB_TEMPLATE = os.path.join(BASE, "web")
|
|
55
|
+
JINJA_TEMPLATE = os.path.join(BASE, "jinja")
|
|
56
|
+
CODE = "code"
|
|
57
|
+
|
|
58
|
+
class Next(SimpleNamespace):
|
|
59
|
+
CONFIG_FILE = "next.config.js"
|
|
60
|
+
SITEMAP_CONFIG_FILE = os.path.join(Dirs.WEB, "next-sitemap.config.js")
|
|
61
|
+
NODE_MODULES = "node_modules"
|
|
62
|
+
PACKAGE_LOCK = "package-lock.json"
|
|
63
|
+
FRONTEND_LISTENING_REGEX = "Local:[\\s]+(.*)"
|
|
64
|
+
|
|
65
|
+
class ColorMode(SimpleNamespace):
|
|
66
|
+
NAME = "colorMode"
|
|
67
|
+
USE = "useColorMode"
|
|
68
|
+
TOGGLE = "toggleColorMode"
|
|
69
|
+
|
|
70
|
+
class Env(str, Enum):
|
|
71
|
+
DEV = "dev"
|
|
72
|
+
PROD = "prod"
|
|
73
|
+
|
|
74
|
+
class LogLevel(str, Enum):
|
|
75
|
+
DEBUG = "debug"
|
|
76
|
+
INFO = "info"
|
|
77
|
+
WARNING = "warning"
|
|
78
|
+
ERROR = "error"
|
|
79
|
+
CRITICAL = "critical"
|
|
80
|
+
|
|
81
|
+
POLLING_MAX_HTTP_BUFFER_SIZE = 1000 * 1000
|
|
82
|
+
|
|
83
|
+
class Ping(SimpleNamespace):
|
|
84
|
+
INTERVAL = 25
|
|
85
|
+
TIMEOUT = 120
|
|
86
|
+
|
|
87
|
+
COOKIES = "cookies"
|
|
88
|
+
LOCAL_STORAGE = "local_storage"
|
|
89
|
+
SKIP_COMPILE_ENV_VAR = "__REFLEX_SKIP_COMPILE"
|
|
90
|
+
ENV_MODE_ENV_VAR = "REFLEX_ENV_MODE"
|
|
91
|
+
PYTEST_CURRENT_TEST = "PYTEST_CURRENT_TEST"
|
|
92
|
+
RELOAD_CONFIG = "__REFLEX_RELOAD_CONFIG"
|
|
93
|
+
REFLEX_VAR_OPENING_TAG = "<reflex.Var>"
|
|
94
|
+
REFLEX_VAR_CLOSING_TAG = "</reflex.Var>"
|
reflex/constants/compiler.py
CHANGED
|
@@ -111,6 +111,14 @@ class Hooks(SimpleNamespace):
|
|
|
111
111
|
"""Common sets of hook declarations."""
|
|
112
112
|
|
|
113
113
|
EVENTS = f"const [{CompileVars.ADD_EVENTS}, {CompileVars.CONNECT_ERROR}] = useContext(EventLoopContext);"
|
|
114
|
+
AUTOFOCUS = """
|
|
115
|
+
// Set focus to the specified element.
|
|
116
|
+
const focusRef = useRef(null)
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
if (focusRef.current) {
|
|
119
|
+
focusRef.current.focus();
|
|
120
|
+
}
|
|
121
|
+
})"""
|
|
114
122
|
|
|
115
123
|
|
|
116
124
|
class MemoizationDisposition(enum.Enum):
|
|
@@ -71,6 +71,28 @@ def _create_package_config(module_name: str, package_name: str):
|
|
|
71
71
|
)
|
|
72
72
|
|
|
73
73
|
|
|
74
|
+
def _get_package_config(exit_on_fail: bool = True) -> dict:
|
|
75
|
+
"""Get the package configuration from the pyproject.toml file.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
exit_on_fail: Whether to exit if the pyproject.toml file is not found.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
The package configuration.
|
|
82
|
+
|
|
83
|
+
Raises:
|
|
84
|
+
Exit: If the pyproject.toml file is not found.
|
|
85
|
+
"""
|
|
86
|
+
try:
|
|
87
|
+
with open(CustomComponents.PYPROJECT_TOML, "rb") as f:
|
|
88
|
+
return dict(tomlkit.load(f))
|
|
89
|
+
except (OSError, TOMLKitError) as ex:
|
|
90
|
+
console.error(f"Unable to read from pyproject.toml due to {ex}")
|
|
91
|
+
if exit_on_fail:
|
|
92
|
+
raise typer.Exit(code=1) from ex
|
|
93
|
+
raise
|
|
94
|
+
|
|
95
|
+
|
|
74
96
|
def _create_readme(module_name: str, package_name: str):
|
|
75
97
|
"""Create a package README file.
|
|
76
98
|
|
|
@@ -145,7 +167,7 @@ def _populate_demo_app(name_variants: NameVariants):
|
|
|
145
167
|
|
|
146
168
|
with set_directory(demo_app_dir):
|
|
147
169
|
# We start with the blank template as basis.
|
|
148
|
-
_init(name=demo_app_name, template=constants.Templates.
|
|
170
|
+
_init(name=demo_app_name, template=constants.Templates.DEFAULT)
|
|
149
171
|
# Then overwrite the app source file with the one we want for testing custom components.
|
|
150
172
|
# This source file is rendered using jinja template file.
|
|
151
173
|
with open(f"{demo_app_name}/{demo_app_name}.py", "w") as f:
|
|
@@ -416,9 +438,7 @@ def _run_commands_in_subprocess(cmds: list[str]) -> bool:
|
|
|
416
438
|
|
|
417
439
|
def _make_pyi_files():
|
|
418
440
|
"""Create pyi files for the custom component."""
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
package_name = glob("custom_components/*.egg-info")[0].replace(".egg-info", "")
|
|
441
|
+
package_name = _get_package_config()["project"]["name"]
|
|
422
442
|
|
|
423
443
|
for dir, _, _ in os.walk(f"./{package_name}"):
|
|
424
444
|
if "__pycache__" in dir:
|
|
@@ -514,22 +534,10 @@ def _validate_credentials(
|
|
|
514
534
|
def _get_version_to_publish() -> str:
|
|
515
535
|
"""Get the version to publish from the pyproject.toml.
|
|
516
536
|
|
|
517
|
-
Raises:
|
|
518
|
-
Exit: If the version is not found in the pyproject.toml.
|
|
519
|
-
|
|
520
537
|
Returns:
|
|
521
538
|
The version to publish.
|
|
522
539
|
"""
|
|
523
|
-
|
|
524
|
-
try:
|
|
525
|
-
with open(CustomComponents.PYPROJECT_TOML, "rb") as f:
|
|
526
|
-
project_toml = tomlkit.parse(f.read())
|
|
527
|
-
return project_toml.get("project", {})["version"]
|
|
528
|
-
except (OSError, KeyError, TOMLKitError) as ex:
|
|
529
|
-
console.error(
|
|
530
|
-
f"Cannot find the version in {CustomComponents.PYPROJECT_TOML} due to {ex}"
|
|
531
|
-
)
|
|
532
|
-
raise typer.Exit(code=1) from ex
|
|
540
|
+
return _get_package_config()["project"]["version"]
|
|
533
541
|
|
|
534
542
|
|
|
535
543
|
def _ensure_dist_dir(version_to_publish: str, build: bool):
|
|
@@ -715,7 +723,7 @@ def publish(
|
|
|
715
723
|
_collect_details_for_gallery()
|
|
716
724
|
|
|
717
725
|
|
|
718
|
-
def _process_entered_list(input: str) -> list | None:
|
|
726
|
+
def _process_entered_list(input: str | None) -> list | None:
|
|
719
727
|
"""Process the user entered comma separated list into a list if applicable.
|
|
720
728
|
|
|
721
729
|
Args:
|
|
@@ -724,7 +732,7 @@ def _process_entered_list(input: str) -> list | None:
|
|
|
724
732
|
Returns:
|
|
725
733
|
The list of items or None.
|
|
726
734
|
"""
|
|
727
|
-
return [t.strip() for t in input.split(",") if t if input] or None
|
|
735
|
+
return [t.strip() for t in (input or "").split(",") if t if input] or None
|
|
728
736
|
|
|
729
737
|
|
|
730
738
|
def _validate_project_info():
|
|
@@ -733,12 +741,7 @@ def _validate_project_info():
|
|
|
733
741
|
Raises:
|
|
734
742
|
Exit: If the pyproject.toml file is ill-formed.
|
|
735
743
|
"""
|
|
736
|
-
|
|
737
|
-
with open(CustomComponents.PYPROJECT_TOML, "rb") as f:
|
|
738
|
-
pyproject_toml = tomlkit.parse(f.read())
|
|
739
|
-
except TOMLKitError as ex:
|
|
740
|
-
console.error(f"Unable to read from pyproject.toml due to {ex}")
|
|
741
|
-
raise typer.Exit(code=1) from ex
|
|
744
|
+
pyproject_toml = _get_package_config()
|
|
742
745
|
|
|
743
746
|
try:
|
|
744
747
|
project = pyproject_toml.get("project", {})
|
|
@@ -773,6 +776,7 @@ def _validate_project_info():
|
|
|
773
776
|
)
|
|
774
777
|
or []
|
|
775
778
|
)
|
|
779
|
+
project["keywords"] = new_keywords
|
|
776
780
|
elif keyword_action == "a":
|
|
777
781
|
new_keywords = (
|
|
778
782
|
_process_entered_list(
|
|
@@ -780,7 +784,7 @@ def _validate_project_info():
|
|
|
780
784
|
)
|
|
781
785
|
or []
|
|
782
786
|
)
|
|
783
|
-
|
|
787
|
+
project["keywords"] = project.get("keywords", []) + new_keywords
|
|
784
788
|
|
|
785
789
|
if not project.get("urls"):
|
|
786
790
|
project["urls"] = {}
|
|
@@ -795,7 +799,7 @@ def _validate_project_info():
|
|
|
795
799
|
with open(CustomComponents.PYPROJECT_TOML, "w") as f:
|
|
796
800
|
tomlkit.dump(pyproject_toml, f)
|
|
797
801
|
except (OSError, TOMLKitError) as ex:
|
|
798
|
-
console.error(f"Unable to
|
|
802
|
+
console.error(f"Unable to write to pyproject.toml due to {ex}")
|
|
799
803
|
raise typer.Exit(code=1) from ex
|
|
800
804
|
|
|
801
805
|
|
|
@@ -815,10 +819,8 @@ def _collect_details_for_gallery():
|
|
|
815
819
|
params = {}
|
|
816
820
|
package_name = None
|
|
817
821
|
try:
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
package_name = project_toml.get("project", {})["name"]
|
|
821
|
-
except (OSError, TOMLKitError, KeyError) as ex:
|
|
822
|
+
package_name = _get_package_config(exit_on_fail=False)["project"]["name"]
|
|
823
|
+
except (TOMLKitError, KeyError) as ex:
|
|
822
824
|
console.debug(
|
|
823
825
|
f"Unable to read from pyproject.toml in current directory due to {ex}"
|
|
824
826
|
)
|
|
@@ -849,13 +851,6 @@ def _collect_details_for_gallery():
|
|
|
849
851
|
console.error(f"Unable to complete request due to {he}.")
|
|
850
852
|
raise typer.Exit(code=1) from he
|
|
851
853
|
|
|
852
|
-
display_name = (
|
|
853
|
-
console.ask("[ Friendly display name for your component ] (enter to skip)")
|
|
854
|
-
or None
|
|
855
|
-
)
|
|
856
|
-
if display_name:
|
|
857
|
-
params["display_name"] = display_name
|
|
858
|
-
|
|
859
854
|
files = []
|
|
860
855
|
if (image_file_and_extension := _get_file_from_prompt_in_loop()) is not None:
|
|
861
856
|
files.append(
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Namespace for experimental features."""
|
|
2
|
+
|
|
3
|
+
from types import SimpleNamespace
|
|
4
|
+
|
|
5
|
+
from ..utils.console import warn
|
|
6
|
+
from . import hooks as hooks
|
|
7
|
+
|
|
8
|
+
warn(
|
|
9
|
+
"`rx._x` contains experimental features and might be removed at any time in the future .",
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
_x = SimpleNamespace(
|
|
13
|
+
hooks=hooks,
|
|
14
|
+
)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Add standard Hooks wrapper for React."""
|
|
2
|
+
|
|
3
|
+
from reflex.utils.imports import ImportVar
|
|
4
|
+
from reflex.vars import Var, VarData
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _add_react_import(v: Var | None, tags: str | list):
|
|
8
|
+
if v is None:
|
|
9
|
+
return
|
|
10
|
+
|
|
11
|
+
if isinstance(tags, str):
|
|
12
|
+
tags = [tags]
|
|
13
|
+
|
|
14
|
+
v._var_data = VarData( # type: ignore
|
|
15
|
+
imports={"react": [ImportVar(tag=tag) for tag in tags]},
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def const(name, value) -> Var | None:
|
|
20
|
+
"""Create a constant Var.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
name: The name of the constant.
|
|
24
|
+
value: The value of the constant.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
The constant Var.
|
|
28
|
+
"""
|
|
29
|
+
return Var.create(f"const {name} = {value}")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def useCallback(func, deps) -> Var | None:
|
|
33
|
+
"""Create a useCallback hook with a function and dependencies.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
func: The function to wrap.
|
|
37
|
+
deps: The dependencies of the function.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
The useCallback hook.
|
|
41
|
+
"""
|
|
42
|
+
if deps:
|
|
43
|
+
v = Var.create(f"useCallback({func}, {deps})")
|
|
44
|
+
else:
|
|
45
|
+
v = Var.create(f"useCallback({func})")
|
|
46
|
+
_add_react_import(v, "useCallback")
|
|
47
|
+
return v
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def useContext(context) -> Var | None:
|
|
51
|
+
"""Create a useContext hook with a context.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
context: The context to use.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
The useContext hook.
|
|
58
|
+
"""
|
|
59
|
+
v = Var.create(f"useContext({context})")
|
|
60
|
+
_add_react_import(v, "useContext")
|
|
61
|
+
return v
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def useRef(default) -> Var | None:
|
|
65
|
+
"""Create a useRef hook with a default value.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
default: The default value of the ref.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
The useRef hook.
|
|
72
|
+
"""
|
|
73
|
+
v = Var.create(f"useRef({default})")
|
|
74
|
+
_add_react_import(v, "useRef")
|
|
75
|
+
return v
|
reflex/page.py
CHANGED
|
@@ -30,7 +30,7 @@ def page(
|
|
|
30
30
|
title: The title of the page.
|
|
31
31
|
image: The favicon of the page.
|
|
32
32
|
description: The description of the page.
|
|
33
|
-
meta:
|
|
33
|
+
meta: Additional meta to add to the page.
|
|
34
34
|
on_load: The event handler(s) called when the page load.
|
|
35
35
|
script_tags: scripts to attach to the page
|
|
36
36
|
|
reflex/reflex.py
CHANGED
|
@@ -63,7 +63,7 @@ def main(
|
|
|
63
63
|
|
|
64
64
|
def _init(
|
|
65
65
|
name: str,
|
|
66
|
-
template:
|
|
66
|
+
template: str | None = None,
|
|
67
67
|
loglevel: constants.LogLevel = config.loglevel,
|
|
68
68
|
):
|
|
69
69
|
"""Initialize a new Reflex app in the given directory."""
|
|
@@ -79,31 +79,20 @@ def _init(
|
|
|
79
79
|
app_name = prerequisites.validate_app_name(name)
|
|
80
80
|
console.rule(f"[bold]Initializing {app_name}")
|
|
81
81
|
|
|
82
|
+
# Check prerequisites.
|
|
82
83
|
prerequisites.check_latest_package_version(constants.Reflex.MODULE_NAME)
|
|
83
|
-
|
|
84
84
|
prerequisites.initialize_reflex_user_directory()
|
|
85
|
-
|
|
86
85
|
prerequisites.ensure_reflex_installation_id()
|
|
87
86
|
|
|
88
87
|
# When upgrading to 0.4, show migration instructions.
|
|
89
88
|
if prerequisites.should_show_rx_chakra_migration_instructions():
|
|
90
89
|
prerequisites.show_rx_chakra_migration_instructions()
|
|
91
90
|
|
|
92
|
-
# Set up the app directory, only if the config doesn't exist.
|
|
93
|
-
if not os.path.exists(constants.Config.FILE):
|
|
94
|
-
if template is None:
|
|
95
|
-
template = prerequisites.prompt_for_template()
|
|
96
|
-
prerequisites.create_config(app_name)
|
|
97
|
-
prerequisites.initialize_app_directory(app_name, template)
|
|
98
|
-
telemetry_event = "init"
|
|
99
|
-
else:
|
|
100
|
-
telemetry_event = "reinit"
|
|
101
|
-
|
|
102
91
|
# Set up the web project.
|
|
103
92
|
prerequisites.initialize_frontend_dependencies()
|
|
104
93
|
|
|
105
|
-
#
|
|
106
|
-
|
|
94
|
+
# Initialize the app.
|
|
95
|
+
prerequisites.initialize_app(app_name, template)
|
|
107
96
|
|
|
108
97
|
# Migrate Pynecone projects to Reflex.
|
|
109
98
|
prerequisites.migrate_to_reflex()
|
|
@@ -123,7 +112,7 @@ def init(
|
|
|
123
112
|
name: str = typer.Option(
|
|
124
113
|
None, metavar="APP_NAME", help="The name of the app to initialize."
|
|
125
114
|
),
|
|
126
|
-
template:
|
|
115
|
+
template: str = typer.Option(
|
|
127
116
|
None,
|
|
128
117
|
help="The template to initialize the app with.",
|
|
129
118
|
),
|
|
@@ -165,7 +154,8 @@ def _run(
|
|
|
165
154
|
_skip_compile()
|
|
166
155
|
|
|
167
156
|
# Check that the app is initialized.
|
|
168
|
-
prerequisites.
|
|
157
|
+
if prerequisites.needs_reinit(frontend=frontend):
|
|
158
|
+
_init(name=config.app_name, loglevel=loglevel)
|
|
169
159
|
|
|
170
160
|
# If something is running on the ports, ask the user if they want to kill or change it.
|
|
171
161
|
if frontend and processes.is_process_on_port(frontend_port):
|
|
@@ -234,6 +224,11 @@ def _run(
|
|
|
234
224
|
# In dev mode, run the backend on the main thread.
|
|
235
225
|
if backend and env == constants.Env.DEV:
|
|
236
226
|
backend_cmd(backend_host, int(backend_port))
|
|
227
|
+
# The windows uvicorn bug workaround
|
|
228
|
+
# https://github.com/reflex-dev/reflex/issues/2335
|
|
229
|
+
if constants.IS_WINDOWS and exec.frontend_process:
|
|
230
|
+
# Sends SIGTERM in windows
|
|
231
|
+
exec.kill(exec.frontend_process.pid)
|
|
237
232
|
|
|
238
233
|
|
|
239
234
|
@cli.command()
|
|
@@ -289,6 +284,10 @@ def export(
|
|
|
289
284
|
):
|
|
290
285
|
"""Export the app to a zip file."""
|
|
291
286
|
from reflex.utils import export as export_utils
|
|
287
|
+
from reflex.utils import prerequisites
|
|
288
|
+
|
|
289
|
+
if prerequisites.needs_reinit(frontend=True):
|
|
290
|
+
_init(name=config.app_name, loglevel=loglevel)
|
|
292
291
|
|
|
293
292
|
export_utils.export(
|
|
294
293
|
zipping=zipping,
|
|
@@ -524,7 +523,8 @@ def deploy(
|
|
|
524
523
|
dependency.check_requirements()
|
|
525
524
|
|
|
526
525
|
# Check if we are set up.
|
|
527
|
-
prerequisites.
|
|
526
|
+
if prerequisites.needs_reinit(frontend=True):
|
|
527
|
+
_init(name=config.app_name, loglevel=loglevel)
|
|
528
528
|
prerequisites.check_latest_package_version(constants.ReflexHostingCLI.MODULE_NAME)
|
|
529
529
|
|
|
530
530
|
hosting_cli.deploy(
|
|
@@ -565,20 +565,6 @@ def demo(
|
|
|
565
565
|
# Open the demo app in a terminal.
|
|
566
566
|
webbrowser.open("https://demo.reflex.run")
|
|
567
567
|
|
|
568
|
-
# Later: open the demo app locally.
|
|
569
|
-
# with tempfile.TemporaryDirectory() as tmp_dir:
|
|
570
|
-
# os.chdir(tmp_dir)
|
|
571
|
-
# _init(
|
|
572
|
-
# name="reflex_demo",
|
|
573
|
-
# template=constants.Templates.Kind.DEMO,
|
|
574
|
-
# loglevel=constants.LogLevel.DEBUG,
|
|
575
|
-
# )
|
|
576
|
-
# _run(
|
|
577
|
-
# frontend_port=frontend_port,
|
|
578
|
-
# backend_port=backend_port,
|
|
579
|
-
# loglevel=constants.LogLevel.DEBUG,
|
|
580
|
-
# )
|
|
581
|
-
|
|
582
568
|
|
|
583
569
|
cli.add_typer(db_cli, name="db", help="Subcommands for managing the database schema.")
|
|
584
570
|
cli.add_typer(script_cli, name="script", help="Subcommands running helper scripts.")
|
reflex/style.py
CHANGED
|
@@ -22,7 +22,7 @@ color_mode_var_data = VarData( # type: ignore
|
|
|
22
22
|
"react": {ImportVar(tag="useContext")},
|
|
23
23
|
},
|
|
24
24
|
hooks={
|
|
25
|
-
f"const [ {constants.ColorMode.NAME}, {constants.ColorMode.TOGGLE} ] = useContext(ColorModeContext)",
|
|
25
|
+
f"const [ {constants.ColorMode.NAME}, {constants.ColorMode.TOGGLE} ] = useContext(ColorModeContext)": None,
|
|
26
26
|
},
|
|
27
27
|
)
|
|
28
28
|
# Var resolves to the current color mode for the app ("light" or "dark")
|
|
@@ -240,9 +240,9 @@ def format_as_emotion(style_dict: dict[str, Any]) -> Style | None:
|
|
|
240
240
|
if isinstance(value, list):
|
|
241
241
|
# Apply media queries from responsive value list.
|
|
242
242
|
mbps = {
|
|
243
|
-
media_query(bp):
|
|
244
|
-
|
|
245
|
-
|
|
243
|
+
media_query(bp): (
|
|
244
|
+
bp_value if isinstance(bp_value, dict) else {key: bp_value}
|
|
245
|
+
)
|
|
246
246
|
for bp, bp_value in enumerate(value)
|
|
247
247
|
}
|
|
248
248
|
if key.startswith("&:"):
|
reflex/testing.py
CHANGED
|
@@ -223,7 +223,7 @@ class AppHarness:
|
|
|
223
223
|
with chdir(self.app_path):
|
|
224
224
|
reflex.reflex._init(
|
|
225
225
|
name=self.app_name,
|
|
226
|
-
template=reflex.constants.Templates.
|
|
226
|
+
template=reflex.constants.Templates.DEFAULT,
|
|
227
227
|
loglevel=reflex.constants.LogLevel.INFO,
|
|
228
228
|
)
|
|
229
229
|
self.app_module_path.write_text(source_code)
|