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/constants/installer.py
CHANGED
|
@@ -35,7 +35,7 @@ class Bun(SimpleNamespace):
|
|
|
35
35
|
"""Bun constants."""
|
|
36
36
|
|
|
37
37
|
# The Bun version.
|
|
38
|
-
VERSION = "1.1.
|
|
38
|
+
VERSION = "1.1.6"
|
|
39
39
|
# Min Bun Version
|
|
40
40
|
MIN_VERSION = "0.7.0"
|
|
41
41
|
# The directory to store the bun.
|
|
@@ -46,6 +46,10 @@ class Bun(SimpleNamespace):
|
|
|
46
46
|
)
|
|
47
47
|
# URL to bun install script.
|
|
48
48
|
INSTALL_URL = "https://bun.sh/install"
|
|
49
|
+
# URL to windows install script.
|
|
50
|
+
WINDOWS_INSTALL_URL = (
|
|
51
|
+
"https://raw.githubusercontent.com/reflex-dev/reflex/main/scripts/install.ps1"
|
|
52
|
+
)
|
|
49
53
|
|
|
50
54
|
|
|
51
55
|
# FNM config.
|
|
@@ -112,6 +116,7 @@ class PackageJson(SimpleNamespace):
|
|
|
112
116
|
"next-themes": "0.2.1",
|
|
113
117
|
"react": "18.2.0",
|
|
114
118
|
"react-dom": "18.2.0",
|
|
119
|
+
"react-focus-lock": "2.11.3",
|
|
115
120
|
"socket.io-client": "4.6.1",
|
|
116
121
|
"universal-cookie": "4.0.4",
|
|
117
122
|
}
|
reflex/constants/route.py
CHANGED
|
@@ -44,6 +44,10 @@ class RouteRegex(SimpleNamespace):
|
|
|
44
44
|
STRICT_CATCHALL = re.compile(r"\[\.{3}([a-zA-Z_][\w]*)\]")
|
|
45
45
|
# group return the arg name (i.e. "slug") (optional arg can be empty)
|
|
46
46
|
OPT_CATCHALL = re.compile(r"\[\[\.{3}([a-zA-Z_][\w]*)\]\]")
|
|
47
|
+
SINGLE_SEGMENT = "__SINGLE_SEGMENT__"
|
|
48
|
+
DOUBLE_SEGMENT = "__DOUBLE_SEGMENT__"
|
|
49
|
+
SINGLE_CATCHALL_SEGMENT = "__SINGLE_CATCHALL_SEGMENT__"
|
|
50
|
+
DOUBLE_CATCHALL_SEGMENT = "__DOUBLE_CATCHALL_SEGMENT__"
|
|
47
51
|
|
|
48
52
|
|
|
49
53
|
class DefaultPage(SimpleNamespace):
|
|
@@ -20,7 +20,6 @@ from reflex import constants
|
|
|
20
20
|
from reflex.config import get_config
|
|
21
21
|
from reflex.constants import CustomComponents
|
|
22
22
|
from reflex.utils import console
|
|
23
|
-
from reflex.utils.pyi_generator import PyiGenerator
|
|
24
23
|
|
|
25
24
|
config = get_config()
|
|
26
25
|
custom_components_cli = typer.Typer()
|
|
@@ -438,6 +437,8 @@ def _run_commands_in_subprocess(cmds: list[str]) -> bool:
|
|
|
438
437
|
|
|
439
438
|
def _make_pyi_files():
|
|
440
439
|
"""Create pyi files for the custom component."""
|
|
440
|
+
from reflex.utils.pyi_generator import PyiGenerator
|
|
441
|
+
|
|
441
442
|
package_name = _get_package_config()["project"]["name"]
|
|
442
443
|
|
|
443
444
|
for dir, _, _ in os.walk(f"./{package_name}"):
|
|
@@ -555,7 +556,6 @@ def _ensure_dist_dir(version_to_publish: str, build: bool):
|
|
|
555
556
|
if build:
|
|
556
557
|
# Need to check if the files here are for the version to be published.
|
|
557
558
|
if dist_dir.exists():
|
|
558
|
-
|
|
559
559
|
# Check if the distribution files are for the version to be published.
|
|
560
560
|
needs_rebuild = False
|
|
561
561
|
for suffix in CustomComponents.DISTRIBUTION_FILE_SUFFIXES:
|
|
@@ -936,3 +936,25 @@ def share_more_detail(
|
|
|
936
936
|
console.set_log_level(loglevel)
|
|
937
937
|
|
|
938
938
|
_collect_details_for_gallery()
|
|
939
|
+
|
|
940
|
+
|
|
941
|
+
@custom_components_cli.command()
|
|
942
|
+
def install(
|
|
943
|
+
loglevel: constants.LogLevel = typer.Option(
|
|
944
|
+
config.loglevel, help="The log level to use."
|
|
945
|
+
),
|
|
946
|
+
):
|
|
947
|
+
"""Install package from this local custom component in editable mode.
|
|
948
|
+
|
|
949
|
+
Args:
|
|
950
|
+
loglevel: The log level to use.
|
|
951
|
+
|
|
952
|
+
Raises:
|
|
953
|
+
Exit: If unable to install the current directory in editable mode.
|
|
954
|
+
"""
|
|
955
|
+
console.set_log_level(loglevel)
|
|
956
|
+
|
|
957
|
+
if _pip_install_on_demand(package_name=".", install_args=["-e"]):
|
|
958
|
+
console.info(f"Package installed successfully!")
|
|
959
|
+
else:
|
|
960
|
+
raise typer.Exit(code=1)
|
reflex/event.py
CHANGED
|
@@ -18,7 +18,7 @@ from typing import (
|
|
|
18
18
|
|
|
19
19
|
from reflex import constants
|
|
20
20
|
from reflex.base import Base
|
|
21
|
-
from reflex.utils import
|
|
21
|
+
from reflex.utils import format
|
|
22
22
|
from reflex.utils.types import ArgsSpec
|
|
23
23
|
from reflex.vars import BaseVar, Var
|
|
24
24
|
|
|
@@ -80,7 +80,7 @@ class EventActionsMixin(Base):
|
|
|
80
80
|
"""Mixin for DOM event actions."""
|
|
81
81
|
|
|
82
82
|
# Whether to `preventDefault` or `stopPropagation` on the event.
|
|
83
|
-
event_actions: Dict[str, bool] = {}
|
|
83
|
+
event_actions: Dict[str, Union[bool, int]] = {}
|
|
84
84
|
|
|
85
85
|
@property
|
|
86
86
|
def stop_propagation(self):
|
|
@@ -104,6 +104,32 @@ class EventActionsMixin(Base):
|
|
|
104
104
|
update={"event_actions": {"preventDefault": True, **self.event_actions}},
|
|
105
105
|
)
|
|
106
106
|
|
|
107
|
+
def throttle(self, limit_ms: int):
|
|
108
|
+
"""Throttle the event handler.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
limit_ms: The time in milliseconds to throttle the event handler.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
New EventHandler-like with throttle set to limit_ms.
|
|
115
|
+
"""
|
|
116
|
+
return self.copy(
|
|
117
|
+
update={"event_actions": {"throttle": limit_ms, **self.event_actions}},
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
def debounce(self, delay_ms: int):
|
|
121
|
+
"""Debounce the event handler.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
delay_ms: The time in milliseconds to debounce the event handler.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
New EventHandler-like with debounce set to delay_ms.
|
|
128
|
+
"""
|
|
129
|
+
return self.copy(
|
|
130
|
+
update={"event_actions": {"debounce": delay_ms, **self.event_actions}},
|
|
131
|
+
)
|
|
132
|
+
|
|
107
133
|
|
|
108
134
|
class EventHandler(EventActionsMixin):
|
|
109
135
|
"""An event handler responds to an event to update the state."""
|
|
@@ -142,7 +168,7 @@ class EventHandler(EventActionsMixin):
|
|
|
142
168
|
"""
|
|
143
169
|
return getattr(self.fn, BACKGROUND_TASK_MARKER, False)
|
|
144
170
|
|
|
145
|
-
def __call__(self, *args:
|
|
171
|
+
def __call__(self, *args: Any) -> EventSpec:
|
|
146
172
|
"""Pass arguments to the handler to get an event spec.
|
|
147
173
|
|
|
148
174
|
This method configures event handlers that take in arguments.
|
|
@@ -169,7 +195,7 @@ class EventHandler(EventActionsMixin):
|
|
|
169
195
|
|
|
170
196
|
# Otherwise, convert to JSON.
|
|
171
197
|
try:
|
|
172
|
-
values.append(Var.create(arg, _var_is_string=
|
|
198
|
+
values.append(Var.create(arg, _var_is_string=isinstance(arg, str)))
|
|
173
199
|
except TypeError as e:
|
|
174
200
|
raise TypeError(
|
|
175
201
|
f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
|
|
@@ -220,6 +246,34 @@ class EventSpec(EventActionsMixin):
|
|
|
220
246
|
event_actions=self.event_actions.copy(),
|
|
221
247
|
)
|
|
222
248
|
|
|
249
|
+
def add_args(self, *args: Var) -> EventSpec:
|
|
250
|
+
"""Add arguments to the event spec.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
*args: The arguments to add positionally.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
The event spec with the new arguments.
|
|
257
|
+
|
|
258
|
+
Raises:
|
|
259
|
+
TypeError: If the arguments are invalid.
|
|
260
|
+
"""
|
|
261
|
+
# Get the remaining unfilled function args.
|
|
262
|
+
fn_args = inspect.getfullargspec(self.handler.fn).args[1 + len(self.args) :]
|
|
263
|
+
fn_args = (Var.create_safe(arg) for arg in fn_args)
|
|
264
|
+
|
|
265
|
+
# Construct the payload.
|
|
266
|
+
values = []
|
|
267
|
+
for arg in args:
|
|
268
|
+
try:
|
|
269
|
+
values.append(Var.create(arg, _var_is_string=isinstance(arg, str)))
|
|
270
|
+
except TypeError as e:
|
|
271
|
+
raise TypeError(
|
|
272
|
+
f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
|
|
273
|
+
) from e
|
|
274
|
+
new_payload = tuple(zip(fn_args, values))
|
|
275
|
+
return self.with_args(self.args + new_payload)
|
|
276
|
+
|
|
223
277
|
|
|
224
278
|
class CallableEventSpec(EventSpec):
|
|
225
279
|
"""Decorate an EventSpec-returning function to act as both a EventSpec and a function.
|
|
@@ -350,7 +404,9 @@ class FileUpload(Base):
|
|
|
350
404
|
]
|
|
351
405
|
elif isinstance(on_upload_progress, Callable):
|
|
352
406
|
# Call the lambda to get the event chain.
|
|
353
|
-
events = call_event_fn(
|
|
407
|
+
events = call_event_fn(
|
|
408
|
+
on_upload_progress, self.on_upload_progress_args_spec
|
|
409
|
+
) # type: ignore
|
|
354
410
|
else:
|
|
355
411
|
raise ValueError(f"{on_upload_progress} is not a valid event handler.")
|
|
356
412
|
on_upload_progress_chain = EventChain(
|
|
@@ -400,7 +456,7 @@ def server_side(name: str, sig: inspect.Signature, **kwargs) -> EventSpec:
|
|
|
400
456
|
return EventSpec(
|
|
401
457
|
handler=EventHandler(fn=fn),
|
|
402
458
|
args=tuple(
|
|
403
|
-
(Var.create_safe(k), Var.create_safe(v, _var_is_string=
|
|
459
|
+
(Var.create_safe(k), Var.create_safe(v, _var_is_string=isinstance(v, str)))
|
|
404
460
|
for k, v in kwargs.items()
|
|
405
461
|
),
|
|
406
462
|
)
|
|
@@ -704,7 +760,8 @@ def get_hydrate_event(state) -> str:
|
|
|
704
760
|
|
|
705
761
|
|
|
706
762
|
def call_event_handler(
|
|
707
|
-
event_handler: EventHandler
|
|
763
|
+
event_handler: EventHandler | EventSpec,
|
|
764
|
+
arg_spec: ArgsSpec,
|
|
708
765
|
) -> EventSpec:
|
|
709
766
|
"""Call an event handler to get the event spec.
|
|
710
767
|
|
|
@@ -722,33 +779,21 @@ def call_event_handler(
|
|
|
722
779
|
Returns:
|
|
723
780
|
The event spec from calling the event handler.
|
|
724
781
|
"""
|
|
725
|
-
|
|
782
|
+
parsed_args = parse_args_spec(arg_spec) # type: ignore
|
|
726
783
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
784
|
+
if isinstance(event_handler, EventSpec):
|
|
785
|
+
# Handle partial application of EventSpec args
|
|
786
|
+
return event_handler.add_args(*parsed_args)
|
|
730
787
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
source = inspect.getsource(arg_spec) # type: ignore
|
|
735
|
-
raise ValueError(
|
|
736
|
-
f"number of arguments in {event_handler.fn.__qualname__} "
|
|
737
|
-
f"doesn't match the definition of the event trigger '{source.strip().strip(',')}'"
|
|
738
|
-
)
|
|
788
|
+
args = inspect.getfullargspec(event_handler.fn).args
|
|
789
|
+
if len(args) == len(["self", *parsed_args]):
|
|
790
|
+
return event_handler(*parsed_args) # type: ignore
|
|
739
791
|
else:
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
removal_version="0.5.0",
|
|
792
|
+
source = inspect.getsource(arg_spec) # type: ignore
|
|
793
|
+
raise ValueError(
|
|
794
|
+
f"number of arguments in {event_handler.fn.__qualname__} "
|
|
795
|
+
f"doesn't match the definition of the event trigger '{source.strip().strip(',')}'"
|
|
745
796
|
)
|
|
746
|
-
if len(args) == 1:
|
|
747
|
-
return event_handler()
|
|
748
|
-
assert (
|
|
749
|
-
len(args) == 2
|
|
750
|
-
), f"Event handler {event_handler.fn} must have 1 or 2 arguments."
|
|
751
|
-
return event_handler(arg_spec) # type: ignore
|
|
752
797
|
|
|
753
798
|
|
|
754
799
|
def parse_args_spec(arg_spec: ArgsSpec):
|
reflex/experimental/__init__.py
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from types import SimpleNamespace
|
|
4
4
|
|
|
5
|
+
from reflex.components.radix.themes.components.progress import progress as progress
|
|
6
|
+
from reflex.components.sonner.toast import toast as toast
|
|
7
|
+
|
|
5
8
|
from ..utils.console import warn
|
|
6
9
|
from . import hooks as hooks
|
|
7
10
|
from .layout import layout as layout
|
|
@@ -14,5 +17,7 @@ warn(
|
|
|
14
17
|
_x = SimpleNamespace(
|
|
15
18
|
hooks=hooks,
|
|
16
19
|
layout=layout,
|
|
20
|
+
progress=progress,
|
|
17
21
|
run_in_thread=run_in_thread,
|
|
22
|
+
toast=toast,
|
|
18
23
|
)
|
reflex/experimental/layout.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""To experiment with layout component, move them to reflex/components later."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
from reflex import color, cond
|
|
4
6
|
from reflex.components.base.fragment import Fragment
|
|
5
7
|
from reflex.components.component import Component, ComponentNamespace, MemoizationLeaf
|
|
@@ -9,6 +11,7 @@ from reflex.components.radix.themes.layout import Box, Container, HStack
|
|
|
9
11
|
from reflex.event import call_script
|
|
10
12
|
from reflex.experimental import hooks
|
|
11
13
|
from reflex.state import ComponentState
|
|
14
|
+
from reflex.style import Style
|
|
12
15
|
from reflex.vars import Var
|
|
13
16
|
|
|
14
17
|
|
|
@@ -26,23 +29,38 @@ class Sidebar(Box, MemoizationLeaf):
|
|
|
26
29
|
Returns:
|
|
27
30
|
The sidebar component.
|
|
28
31
|
"""
|
|
29
|
-
props.setdefault("border_right", f"1px solid {color('accent', 12)}")
|
|
30
|
-
props.setdefault("background_color", color("accent", 1))
|
|
31
|
-
props.setdefault("width", "20vw")
|
|
32
|
-
props.setdefault("height", "100vh")
|
|
33
|
-
props.setdefault("position", "fixed")
|
|
32
|
+
# props.setdefault("border_right", f"1px solid {color('accent', 12)}")
|
|
33
|
+
# props.setdefault("background_color", color("accent", 1))
|
|
34
|
+
# props.setdefault("width", "20vw")
|
|
35
|
+
# props.setdefault("height", "100vh")
|
|
36
|
+
# props.setdefault("position", "fixed")
|
|
34
37
|
|
|
35
38
|
return super().create(
|
|
36
39
|
Box.create(*children, **props), # sidebar for content
|
|
37
40
|
Box.create(width=props.get("width")), # spacer for layout
|
|
38
41
|
)
|
|
39
42
|
|
|
40
|
-
def
|
|
43
|
+
def add_style(self) -> Style | None:
|
|
44
|
+
"""Add style to the component.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
The style of the component.
|
|
48
|
+
"""
|
|
41
49
|
sidebar: Component = self.children[-2] # type: ignore
|
|
42
50
|
spacer: Component = self.children[-1] # type: ignore
|
|
43
51
|
open = self.State.open if self.State else Var.create("open") # type: ignore
|
|
44
52
|
sidebar.style["display"] = spacer.style["display"] = cond(open, "block", "none")
|
|
45
53
|
|
|
54
|
+
return Style(
|
|
55
|
+
{
|
|
56
|
+
"position": "fixed",
|
|
57
|
+
"border_right": f"1px solid {color('accent', 12)}",
|
|
58
|
+
"background_color": color("accent", 1),
|
|
59
|
+
"width": "20vw",
|
|
60
|
+
"height": "100vh",
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
|
|
46
64
|
def _get_hooks(self) -> Var | None:
|
|
47
65
|
return hooks.useState("open", "true") if not self.State else None
|
|
48
66
|
|
reflex/model.py
CHANGED
|
@@ -133,7 +133,7 @@ class ModelRegistry:
|
|
|
133
133
|
return metadata
|
|
134
134
|
|
|
135
135
|
|
|
136
|
-
class Model(Base, sqlmodel.SQLModel):
|
|
136
|
+
class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssues]
|
|
137
137
|
"""Base class to define a table in the database."""
|
|
138
138
|
|
|
139
139
|
# The primary key for the table.
|
|
@@ -310,6 +310,7 @@ class Model(Base, sqlmodel.SQLModel):
|
|
|
310
310
|
render_item=cls._alembic_render_item,
|
|
311
311
|
process_revision_directives=writer, # type: ignore
|
|
312
312
|
compare_type=False,
|
|
313
|
+
render_as_batch=True, # for sqlite compatibility
|
|
313
314
|
)
|
|
314
315
|
env.run_migrations()
|
|
315
316
|
changes_detected = False
|
reflex/page.py
CHANGED
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from
|
|
5
|
+
from collections import defaultdict
|
|
6
|
+
from typing import Any, Dict, List
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
from reflex.config import get_config
|
|
9
|
+
|
|
10
|
+
DECORATED_PAGES: Dict[str, List] = defaultdict(list)
|
|
8
11
|
|
|
9
12
|
|
|
10
13
|
def page(
|
|
@@ -55,7 +58,7 @@ def page(
|
|
|
55
58
|
if on_load:
|
|
56
59
|
kwargs["on_load"] = on_load
|
|
57
60
|
|
|
58
|
-
DECORATED_PAGES.append((render_fn, kwargs))
|
|
61
|
+
DECORATED_PAGES[get_config().app_name].append((render_fn, kwargs))
|
|
59
62
|
|
|
60
63
|
return render_fn
|
|
61
64
|
|
|
@@ -69,6 +72,6 @@ def get_decorated_pages() -> list[dict]:
|
|
|
69
72
|
The decorated pages.
|
|
70
73
|
"""
|
|
71
74
|
return sorted(
|
|
72
|
-
[page_data for
|
|
75
|
+
[page_data for _, page_data in DECORATED_PAGES[get_config().app_name]],
|
|
73
76
|
key=lambda x: x["route"],
|
|
74
77
|
)
|
reflex/reflex.py
CHANGED
|
@@ -157,7 +157,7 @@ def _run(
|
|
|
157
157
|
if prerequisites.needs_reinit(frontend=frontend):
|
|
158
158
|
_init(name=config.app_name, loglevel=loglevel)
|
|
159
159
|
|
|
160
|
-
#
|
|
160
|
+
# Find the next available open port.
|
|
161
161
|
if frontend and processes.is_process_on_port(frontend_port):
|
|
162
162
|
frontend_port = processes.change_port(frontend_port, "frontend")
|
|
163
163
|
|
|
@@ -212,7 +212,7 @@ def _run(
|
|
|
212
212
|
# Run the frontend on a separate thread.
|
|
213
213
|
if frontend:
|
|
214
214
|
setup_frontend(Path.cwd())
|
|
215
|
-
commands.append((frontend_cmd, Path.cwd(), frontend_port))
|
|
215
|
+
commands.append((frontend_cmd, Path.cwd(), frontend_port, backend))
|
|
216
216
|
|
|
217
217
|
# In prod mode, run the backend on a separate thread.
|
|
218
218
|
if backend and env == constants.Env.PROD:
|
|
@@ -528,7 +528,12 @@ def deploy(
|
|
|
528
528
|
|
|
529
529
|
hosting_cli.deploy(
|
|
530
530
|
app_name=app_name,
|
|
531
|
-
export_fn=lambda zip_dest_dir,
|
|
531
|
+
export_fn=lambda zip_dest_dir,
|
|
532
|
+
api_url,
|
|
533
|
+
deploy_url,
|
|
534
|
+
frontend,
|
|
535
|
+
backend,
|
|
536
|
+
zipping: export_utils.export(
|
|
532
537
|
zip_dest_dir=zip_dest_dir,
|
|
533
538
|
api_url=api_url,
|
|
534
539
|
deploy_url=deploy_url,
|
reflex/route.py
CHANGED
|
@@ -101,3 +101,42 @@ def catchall_prefix(route: str) -> str:
|
|
|
101
101
|
"""
|
|
102
102
|
pattern = catchall_in_route(route)
|
|
103
103
|
return route.replace(pattern, "") if pattern else ""
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def replace_brackets_with_keywords(input_string):
|
|
107
|
+
"""Replace brackets and everything inside it in a string with a keyword.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
input_string: String to replace.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
new string containing keywords.
|
|
114
|
+
"""
|
|
115
|
+
# /posts -> /post
|
|
116
|
+
# /posts/[slug] -> /posts/__SINGLE_SEGMENT__
|
|
117
|
+
# /posts/[slug]/comments -> /posts/__SINGLE_SEGMENT__/comments
|
|
118
|
+
# /posts/[[slug]] -> /posts/__DOUBLE_SEGMENT__
|
|
119
|
+
# / posts/[[...slug2]]-> /posts/__DOUBLE_CATCHALL_SEGMENT__
|
|
120
|
+
# /posts/[...slug3]-> /posts/__SINGLE_CATCHALL_SEGMENT__
|
|
121
|
+
|
|
122
|
+
# Replace [[...<slug>]] with __DOUBLE_CATCHALL_SEGMENT__
|
|
123
|
+
output_string = re.sub(
|
|
124
|
+
r"\[\[\.\.\..+?\]\]",
|
|
125
|
+
constants.RouteRegex.DOUBLE_CATCHALL_SEGMENT,
|
|
126
|
+
input_string,
|
|
127
|
+
)
|
|
128
|
+
# Replace [...<slug>] with __SINGLE_CATCHALL_SEGMENT__
|
|
129
|
+
output_string = re.sub(
|
|
130
|
+
r"\[\.\.\..+?\]",
|
|
131
|
+
constants.RouteRegex.SINGLE_CATCHALL_SEGMENT,
|
|
132
|
+
output_string,
|
|
133
|
+
)
|
|
134
|
+
# Replace [[<slug>]] with __DOUBLE_SEGMENT__
|
|
135
|
+
output_string = re.sub(
|
|
136
|
+
r"\[\[.+?\]\]", constants.RouteRegex.DOUBLE_SEGMENT, output_string
|
|
137
|
+
)
|
|
138
|
+
# Replace [<slug>] with __SINGLE_SEGMENT__
|
|
139
|
+
output_string = re.sub(
|
|
140
|
+
r"\[.+?\]", constants.RouteRegex.SINGLE_SEGMENT, output_string
|
|
141
|
+
)
|
|
142
|
+
return output_string
|