reflex 0.5.4a3__py3-none-any.whl → 0.5.5__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (260) hide show
  1. reflex/.templates/apps/demo/code/demo.py +1 -0
  2. reflex/.templates/apps/demo/code/pages/__init__.py +1 -0
  3. reflex/.templates/apps/demo/code/pages/datatable.py +1 -0
  4. reflex/.templates/apps/demo/code/pages/forms.py +1 -0
  5. reflex/.templates/apps/demo/code/pages/graphing.py +1 -0
  6. reflex/.templates/apps/demo/code/pages/home.py +1 -0
  7. reflex/.templates/apps/demo/code/styles.py +1 -0
  8. reflex/.templates/apps/demo/code/webui/components/loading_icon.py +1 -8
  9. reflex/.templates/web/components/reflex/chakra_color_mode_provider.js +27 -12
  10. reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +19 -5
  11. reflex/.templates/web/utils/state.js +73 -7
  12. reflex/__init__.py +3 -0
  13. reflex/__init__.pyi +3 -0
  14. reflex/admin.py +1 -0
  15. reflex/app.py +8 -6
  16. reflex/app_module_for_backend.py +2 -1
  17. reflex/base.py +4 -4
  18. reflex/compiler/compiler.py +4 -3
  19. reflex/compiler/templates.py +2 -0
  20. reflex/compiler/utils.py +58 -35
  21. reflex/components/__init__.py +1 -0
  22. reflex/components/base/__init__.py +1 -0
  23. reflex/components/base/app_wrap.py +1 -0
  24. reflex/components/base/fragment.py +1 -0
  25. reflex/components/base/link.py +0 -1
  26. reflex/components/base/script.py +11 -15
  27. reflex/components/base/script.pyi +1 -2
  28. reflex/components/chakra/base.py +15 -13
  29. reflex/components/chakra/base.pyi +3 -1
  30. reflex/components/chakra/datadisplay/code.py +1 -0
  31. reflex/components/chakra/datadisplay/divider.py +1 -0
  32. reflex/components/chakra/datadisplay/table.py +1 -0
  33. reflex/components/chakra/datadisplay/table.pyi +3 -0
  34. reflex/components/chakra/datadisplay/tag.py +1 -0
  35. reflex/components/chakra/disclosure/transition.py +1 -0
  36. reflex/components/chakra/feedback/circularprogress.py +1 -0
  37. reflex/components/chakra/forms/button.py +1 -0
  38. reflex/components/chakra/forms/checkbox.py +4 -13
  39. reflex/components/chakra/forms/checkbox.pyi +1 -3
  40. reflex/components/chakra/forms/colormodeswitch.py +1 -0
  41. reflex/components/chakra/forms/editable.py +13 -16
  42. reflex/components/chakra/forms/editable.pyi +1 -3
  43. reflex/components/chakra/forms/form.py +1 -0
  44. reflex/components/chakra/forms/input.py +22 -21
  45. reflex/components/chakra/forms/input.pyi +4 -4
  46. reflex/components/chakra/forms/multiselect.py +1 -0
  47. reflex/components/chakra/forms/numberinput.py +3 -12
  48. reflex/components/chakra/forms/numberinput.pyi +1 -3
  49. reflex/components/chakra/forms/pininput.py +8 -14
  50. reflex/components/chakra/forms/pininput.pyi +2 -3
  51. reflex/components/chakra/forms/radio.py +4 -13
  52. reflex/components/chakra/forms/radio.pyi +2 -3
  53. reflex/components/chakra/forms/rangeslider.py +10 -13
  54. reflex/components/chakra/forms/rangeslider.pyi +2 -3
  55. reflex/components/chakra/forms/select.py +4 -12
  56. reflex/components/chakra/forms/select.pyi +2 -3
  57. reflex/components/chakra/forms/slider.py +10 -13
  58. reflex/components/chakra/forms/slider.pyi +2 -3
  59. reflex/components/chakra/forms/switch.py +4 -13
  60. reflex/components/chakra/forms/switch.pyi +1 -3
  61. reflex/components/chakra/forms/textarea.py +16 -17
  62. reflex/components/chakra/forms/textarea.pyi +1 -3
  63. reflex/components/chakra/media/avatar.py +4 -12
  64. reflex/components/chakra/media/avatar.pyi +1 -2
  65. reflex/components/chakra/media/icon.py +1 -0
  66. reflex/components/chakra/media/image.py +7 -11
  67. reflex/components/chakra/media/image.pyi +2 -2
  68. reflex/components/chakra/navigation/link.py +8 -4
  69. reflex/components/chakra/navigation/link.pyi +2 -1
  70. reflex/components/chakra/overlay/alertdialog.py +12 -14
  71. reflex/components/chakra/overlay/alertdialog.pyi +1 -2
  72. reflex/components/chakra/overlay/drawer.py +12 -14
  73. reflex/components/chakra/overlay/drawer.pyi +1 -2
  74. reflex/components/chakra/overlay/menu.py +7 -11
  75. reflex/components/chakra/overlay/menu.pyi +2 -2
  76. reflex/components/chakra/overlay/modal.py +13 -13
  77. reflex/components/chakra/overlay/modal.pyi +2 -2
  78. reflex/components/chakra/overlay/popover.py +6 -12
  79. reflex/components/chakra/overlay/popover.pyi +1 -2
  80. reflex/components/chakra/overlay/tooltip.py +7 -13
  81. reflex/components/chakra/overlay/tooltip.pyi +1 -2
  82. reflex/components/chakra/typography/heading.py +0 -1
  83. reflex/components/chakra/typography/span.py +1 -0
  84. reflex/components/chakra/typography/text.py +1 -0
  85. reflex/components/component.py +70 -43
  86. reflex/components/core/__init__.py +1 -0
  87. reflex/components/core/banner.py +27 -24
  88. reflex/components/core/banner.pyi +6 -2
  89. reflex/components/core/client_side_routing.py +1 -0
  90. reflex/components/core/cond.py +19 -17
  91. reflex/components/core/debounce.py +4 -11
  92. reflex/components/core/debounce.pyi +1 -1
  93. reflex/components/core/foreach.py +1 -0
  94. reflex/components/core/html.py +1 -0
  95. reflex/components/core/match.py +9 -6
  96. reflex/components/core/upload.py +26 -25
  97. reflex/components/core/upload.pyi +3 -4
  98. reflex/components/datadisplay/__init__.py +1 -0
  99. reflex/components/datadisplay/code.py +27 -23
  100. reflex/components/datadisplay/code.pyi +4 -2
  101. reflex/components/datadisplay/dataeditor.py +73 -80
  102. reflex/components/datadisplay/dataeditor.pyi +52 -4
  103. reflex/components/datadisplay/logo.py +1 -0
  104. reflex/components/el/__init__.py +1 -0
  105. reflex/components/el/__init__.pyi +8 -0
  106. reflex/components/el/element.py +0 -1
  107. reflex/components/el/elements/__init__.py +5 -0
  108. reflex/components/el/elements/__init__.pyi +12 -1
  109. reflex/components/el/elements/base.py +1 -0
  110. reflex/components/el/elements/forms.py +44 -61
  111. reflex/components/el/elements/forms.pyi +4 -6
  112. reflex/components/el/elements/inline.py +1 -0
  113. reflex/components/el/elements/media.py +53 -0
  114. reflex/components/el/elements/media.pyi +428 -0
  115. reflex/components/el/elements/metadata.py +11 -0
  116. reflex/components/el/elements/metadata.pyi +80 -0
  117. reflex/components/el/elements/other.py +1 -0
  118. reflex/components/el/elements/scripts.py +1 -0
  119. reflex/components/el/elements/tables.py +1 -0
  120. reflex/components/el/elements/typography.py +1 -0
  121. reflex/components/gridjs/datatable.py +9 -6
  122. reflex/components/gridjs/datatable.pyi +4 -1
  123. reflex/components/markdown/markdown.py +36 -41
  124. reflex/components/markdown/markdown.pyi +4 -3
  125. reflex/components/media/icon.py +1 -0
  126. reflex/components/moment/moment.py +11 -17
  127. reflex/components/moment/moment.pyi +4 -3
  128. reflex/components/next/base.py +1 -0
  129. reflex/components/next/image.py +6 -11
  130. reflex/components/next/image.pyi +2 -2
  131. reflex/components/plotly/plotly.py +1 -0
  132. reflex/components/props.py +1 -0
  133. reflex/components/radix/__init__.py +1 -0
  134. reflex/components/radix/primitives/__init__.py +1 -0
  135. reflex/components/radix/primitives/accordion.py +7 -15
  136. reflex/components/radix/primitives/accordion.pyi +7 -4
  137. reflex/components/radix/primitives/base.py +1 -0
  138. reflex/components/radix/primitives/drawer.py +17 -27
  139. reflex/components/radix/primitives/drawer.pyi +2 -4
  140. reflex/components/radix/primitives/form.py +4 -12
  141. reflex/components/radix/primitives/form.pyi +2 -3
  142. reflex/components/radix/primitives/slider.py +6 -11
  143. reflex/components/radix/primitives/slider.pyi +2 -2
  144. reflex/components/radix/themes/__init__.py +1 -0
  145. reflex/components/radix/themes/base.py +3 -3
  146. reflex/components/radix/themes/base.pyi +3 -2
  147. reflex/components/radix/themes/color_mode.py +31 -2
  148. reflex/components/radix/themes/color_mode.pyi +10 -1
  149. reflex/components/radix/themes/components/__init__.py +1 -0
  150. reflex/components/radix/themes/components/alert_dialog.py +13 -24
  151. reflex/components/radix/themes/components/alert_dialog.pyi +2 -4
  152. reflex/components/radix/themes/components/aspect_ratio.py +1 -0
  153. reflex/components/radix/themes/components/card.py +1 -0
  154. reflex/components/radix/themes/components/checkbox.py +6 -22
  155. reflex/components/radix/themes/components/checkbox.pyi +2 -4
  156. reflex/components/radix/themes/components/checkbox_group.py +15 -3
  157. reflex/components/radix/themes/components/checkbox_group.pyi +10 -2
  158. reflex/components/radix/themes/components/context_menu.py +29 -38
  159. reflex/components/radix/themes/components/context_menu.pyi +2 -5
  160. reflex/components/radix/themes/components/dialog.py +18 -26
  161. reflex/components/radix/themes/components/dialog.pyi +2 -4
  162. reflex/components/radix/themes/components/dropdown_menu.py +32 -57
  163. reflex/components/radix/themes/components/dropdown_menu.pyi +2 -7
  164. reflex/components/radix/themes/components/hover_card.py +5 -12
  165. reflex/components/radix/themes/components/hover_card.pyi +2 -3
  166. reflex/components/radix/themes/components/icon_button.py +1 -0
  167. reflex/components/radix/themes/components/icon_button.pyi +1 -0
  168. reflex/components/radix/themes/components/inset.py +1 -0
  169. reflex/components/radix/themes/components/popover.py +22 -27
  170. reflex/components/radix/themes/components/popover.pyi +2 -4
  171. reflex/components/radix/themes/components/radio_group.py +25 -17
  172. reflex/components/radix/themes/components/radio_group.pyi +2 -3
  173. reflex/components/radix/themes/components/scroll_area.py +1 -0
  174. reflex/components/radix/themes/components/segmented_control.py +18 -5
  175. reflex/components/radix/themes/components/segmented_control.pyi +16 -7
  176. reflex/components/radix/themes/components/select.py +13 -23
  177. reflex/components/radix/themes/components/select.pyi +1 -4
  178. reflex/components/radix/themes/components/separator.py +1 -0
  179. reflex/components/radix/themes/components/slider.py +7 -12
  180. reflex/components/radix/themes/components/slider.pyi +2 -3
  181. reflex/components/radix/themes/components/switch.py +5 -12
  182. reflex/components/radix/themes/components/switch.pyi +2 -3
  183. reflex/components/radix/themes/components/table.py +1 -0
  184. reflex/components/radix/themes/components/tabs.py +4 -11
  185. reflex/components/radix/themes/components/tabs.pyi +2 -2
  186. reflex/components/radix/themes/components/text_area.py +19 -18
  187. reflex/components/radix/themes/components/text_area.pyi +2 -3
  188. reflex/components/radix/themes/components/text_field.py +19 -18
  189. reflex/components/radix/themes/components/text_field.pyi +3 -3
  190. reflex/components/radix/themes/components/tooltip.py +10 -13
  191. reflex/components/radix/themes/components/tooltip.pyi +2 -3
  192. reflex/components/radix/themes/layout/__init__.py +1 -0
  193. reflex/components/radix/themes/layout/box.py +1 -0
  194. reflex/components/radix/themes/layout/container.py +1 -0
  195. reflex/components/radix/themes/layout/list.py +1 -0
  196. reflex/components/radix/themes/layout/list.pyi +1 -0
  197. reflex/components/radix/themes/layout/section.py +1 -0
  198. reflex/components/radix/themes/typography/__init__.py +1 -0
  199. reflex/components/radix/themes/typography/base.py +1 -0
  200. reflex/components/radix/themes/typography/blockquote.py +1 -0
  201. reflex/components/radix/themes/typography/code.py +1 -0
  202. reflex/components/radix/themes/typography/heading.py +1 -0
  203. reflex/components/radix/themes/typography/link.py +8 -3
  204. reflex/components/radix/themes/typography/link.pyi +2 -1
  205. reflex/components/react_player/audio.py +1 -0
  206. reflex/components/react_player/audio.pyi +48 -0
  207. reflex/components/react_player/react_player.py +49 -0
  208. reflex/components/react_player/react_player.pyi +49 -0
  209. reflex/components/react_player/video.py +1 -0
  210. reflex/components/react_player/video.pyi +48 -0
  211. reflex/components/recharts/__init__.py +1 -0
  212. reflex/components/recharts/cartesian.py +264 -74
  213. reflex/components/recharts/cartesian.pyi +573 -58
  214. reflex/components/recharts/charts.py +68 -78
  215. reflex/components/recharts/charts.pyi +373 -156
  216. reflex/components/recharts/general.py +52 -20
  217. reflex/components/recharts/general.pyi +52 -6
  218. reflex/components/recharts/polar.py +30 -18
  219. reflex/components/recharts/polar.pyi +66 -5
  220. reflex/components/recharts/recharts.py +5 -3
  221. reflex/components/recharts/recharts.pyi +2 -1
  222. reflex/components/sonner/toast.py +2 -2
  223. reflex/components/sonner/toast.pyi +1 -1
  224. reflex/components/suneditor/editor.py +39 -26
  225. reflex/components/suneditor/editor.pyi +4 -4
  226. reflex/components/tags/iter_tag.py +1 -0
  227. reflex/constants/__init__.py +3 -2
  228. reflex/constants/base.py +20 -21
  229. reflex/constants/compiler.py +3 -1
  230. reflex/constants/config.py +1 -0
  231. reflex/constants/event.py +1 -0
  232. reflex/constants/installer.py +3 -2
  233. reflex/constants/style.py +2 -8
  234. reflex/event.py +36 -6
  235. reflex/experimental/assets.py +1 -0
  236. reflex/experimental/client_state.py +9 -3
  237. reflex/experimental/hooks.py +1 -0
  238. reflex/experimental/misc.py +12 -3
  239. reflex/middleware/hydrate_middleware.py +1 -0
  240. reflex/middleware/middleware.py +1 -0
  241. reflex/state.py +38 -1
  242. reflex/style.py +67 -20
  243. reflex/testing.py +6 -2
  244. reflex/utils/build.py +76 -72
  245. reflex/utils/exec.py +17 -9
  246. reflex/utils/export.py +13 -9
  247. reflex/utils/imports.py +34 -5
  248. reflex/utils/lazy_loader.py +1 -0
  249. reflex/utils/path_ops.py +39 -33
  250. reflex/utils/prerequisites.py +38 -29
  251. reflex/utils/processes.py +1 -1
  252. reflex/utils/serializers.py +3 -6
  253. reflex/utils/watch.py +3 -1
  254. reflex/vars.py +26 -10
  255. reflex/vars.pyi +3 -3
  256. {reflex-0.5.4a3.dist-info → reflex-0.5.5.dist-info}/METADATA +3 -3
  257. {reflex-0.5.4a3.dist-info → reflex-0.5.5.dist-info}/RECORD +260 -260
  258. {reflex-0.5.4a3.dist-info → reflex-0.5.5.dist-info}/LICENSE +0 -0
  259. {reflex-0.5.4a3.dist-info → reflex-0.5.5.dist-info}/WHEEL +0 -0
  260. {reflex-0.5.4a3.dist-info → reflex-0.5.5.dist-info}/entry_points.txt +0 -0
@@ -1,4 +1,5 @@
1
1
  """Welcome to Reflex! This file outlines the steps to create a basic app."""
2
+
2
3
  from typing import Callable
3
4
 
4
5
  import reflex as rx
@@ -1,4 +1,5 @@
1
1
  """The pages of the app."""
2
+
2
3
  from .chatapp import chatapp_page
3
4
  from .datatable import datatable_page
4
5
  from .forms import forms_page
@@ -1,4 +1,5 @@
1
1
  """The settings page for the template."""
2
+
2
3
  from typing import Any
3
4
 
4
5
  import reflex as rx
@@ -1,4 +1,5 @@
1
1
  """The settings page for the template."""
2
+
2
3
  import reflex as rx
3
4
 
4
5
  from ..states.form_state import FormState, UploadState
@@ -1,4 +1,5 @@
1
1
  """The dashboard page for the template."""
2
+
2
3
  import reflex as rx
3
4
 
4
5
  from ..states.pie_state import PieChartState
@@ -1,4 +1,5 @@
1
1
  """The home page of the app."""
2
+
2
3
  import reflex as rx
3
4
 
4
5
  from ..styles import *
@@ -1,4 +1,5 @@
1
1
  """Styles for the app."""
2
+
2
3
  import reflex as rx
3
4
 
4
5
  from .state import State
@@ -13,14 +13,7 @@ class LoadingIcon(rx.Component):
13
13
  stroke_width: rx.Var[str]
14
14
  speed: rx.Var[str]
15
15
  height: rx.Var[str]
16
-
17
- def get_event_triggers(self) -> dict:
18
- """Get the event triggers that pass the component's value to the handler.
19
-
20
- Returns:
21
- A dict mapping the event trigger to the var that is passed to the handler.
22
- """
23
- return {"on_change": lambda status: [status]}
16
+ on_change: rx.EventHandler[lambda status: [status]]
24
17
 
25
18
 
26
19
  loading_icon = LoadingIcon.create
@@ -1,21 +1,36 @@
1
- import { useColorMode as chakraUseColorMode } from "@chakra-ui/react"
2
- import { useTheme } from "next-themes"
3
- import { useEffect } from "react"
4
- import { ColorModeContext } from "/utils/context.js"
1
+ import { useColorMode as chakraUseColorMode } from "@chakra-ui/react";
2
+ import { useTheme } from "next-themes";
3
+ import { useEffect, useState } from "react";
4
+ import { ColorModeContext, defaultColorMode } from "/utils/context.js";
5
5
 
6
6
  export default function ChakraColorModeProvider({ children }) {
7
- const {colorMode, toggleColorMode} = chakraUseColorMode()
8
- const {theme, setTheme} = useTheme()
7
+ const { theme, resolvedTheme, setTheme } = useTheme();
8
+ const { colorMode, toggleColorMode } = chakraUseColorMode();
9
+ const [resolvedColorMode, setResolvedColorMode] = useState(colorMode);
9
10
 
10
11
  useEffect(() => {
11
- if (colorMode != theme) {
12
- toggleColorMode()
12
+ if (colorMode != resolvedTheme) {
13
+ toggleColorMode();
13
14
  }
14
- }, [theme])
15
+ setResolvedColorMode(resolvedTheme);
16
+ }, [theme, resolvedTheme]);
15
17
 
18
+ const rawColorMode = colorMode;
19
+ const setColorMode = (mode) => {
20
+ const allowedModes = ["light", "dark", "system"];
21
+ if (!allowedModes.includes(mode)) {
22
+ console.error(
23
+ `Invalid color mode "${mode}". Defaulting to "${defaultColorMode}".`
24
+ );
25
+ mode = defaultColorMode;
26
+ }
27
+ setTheme(mode);
28
+ };
16
29
  return (
17
- <ColorModeContext.Provider value={[ colorMode, toggleColorMode ]}>
30
+ <ColorModeContext.Provider
31
+ value={{ rawColorMode, resolvedColorMode, toggleColorMode, setColorMode }}
32
+ >
18
33
  {children}
19
34
  </ColorModeContext.Provider>
20
- )
21
- }
35
+ );
36
+ }
@@ -3,18 +3,32 @@ import { useEffect, useState } from "react";
3
3
  import { ColorModeContext, defaultColorMode } from "/utils/context.js";
4
4
 
5
5
  export default function RadixThemesColorModeProvider({ children }) {
6
- const { resolvedTheme, setTheme } = useTheme();
7
- const [colorMode, setColorMode] = useState(defaultColorMode);
6
+ const { theme, resolvedTheme, setTheme } = useTheme();
7
+ const [rawColorMode, setRawColorMode] = useState(defaultColorMode);
8
+ const [resolvedColorMode, setResolvedColorMode] = useState("dark");
8
9
 
9
10
  useEffect(() => {
10
- setColorMode(resolvedTheme);
11
- }, [resolvedTheme]);
11
+ setRawColorMode(theme);
12
+ setResolvedColorMode(resolvedTheme);
13
+ }, [theme, resolvedTheme]);
12
14
 
13
15
  const toggleColorMode = () => {
14
16
  setTheme(resolvedTheme === "light" ? "dark" : "light");
15
17
  };
18
+ const setColorMode = (mode) => {
19
+ const allowedModes = ["light", "dark", "system"];
20
+ if (!allowedModes.includes(mode)) {
21
+ console.error(
22
+ `Invalid color mode "${mode}". Defaulting to "${defaultColorMode}".`
23
+ );
24
+ mode = defaultColorMode;
25
+ }
26
+ setTheme(mode);
27
+ };
16
28
  return (
17
- <ColorModeContext.Provider value={[colorMode, toggleColorMode]}>
29
+ <ColorModeContext.Provider
30
+ value={{ rawColorMode, resolvedColorMode, toggleColorMode, setColorMode }}
31
+ >
18
32
  {children}
19
33
  </ColorModeContext.Provider>
20
34
  );
@@ -107,6 +107,18 @@ export const getBackendURL = (url_str) => {
107
107
  return endpoint;
108
108
  };
109
109
 
110
+ /**
111
+ * Determine if any event in the event queue is stateful.
112
+ *
113
+ * @returns True if there's any event that requires state and False if none of them do.
114
+ */
115
+ export const isStateful = () => {
116
+ if (event_queue.length === 0) {
117
+ return false;
118
+ }
119
+ return event_queue.some(event => event.name.startsWith("state"));
120
+ }
121
+
110
122
  /**
111
123
  * Apply a delta to the state.
112
124
  * @param state The state to apply the delta to.
@@ -116,6 +128,20 @@ export const applyDelta = (state, delta) => {
116
128
  return { ...state, ...delta };
117
129
  };
118
130
 
131
+ /**
132
+ * Only Queue and process events when websocket connection exists.
133
+ * @param event The event to queue.
134
+ * @param socket The socket object to send the event on.
135
+ *
136
+ * @returns Adds event to queue and processes it if websocket exits, does nothing otherwise.
137
+ */
138
+ export const queueEventIfSocketExists = async (events, socket) => {
139
+ if (!socket) {
140
+ return;
141
+ }
142
+ await queueEvents(events, socket);
143
+ }
144
+
119
145
  /**
120
146
  * Handle frontend event or send the event to the backend via Websocket.
121
147
  * @param event The event to send.
@@ -143,18 +169,30 @@ export const applyEvent = async (event, socket) => {
143
169
 
144
170
  if (event.name == "_remove_cookie") {
145
171
  cookies.remove(event.payload.key, { ...event.payload.options });
146
- queueEvents(initialEvents(), socket);
172
+ queueEventIfSocketExists(initialEvents(), socket);
147
173
  return false;
148
174
  }
149
175
 
150
176
  if (event.name == "_clear_local_storage") {
151
177
  localStorage.clear();
152
- queueEvents(initialEvents(), socket);
178
+ queueEventIfSocketExists(initialEvents(), socket);
153
179
  return false;
154
180
  }
155
181
 
156
182
  if (event.name == "_remove_local_storage") {
157
183
  localStorage.removeItem(event.payload.key);
184
+ queueEventIfSocketExists(initialEvents(), socket);
185
+ return false;
186
+ }
187
+
188
+ if (event.name == "_clear_session_storage") {
189
+ sessionStorage.clear();
190
+ queueEvents(initialEvents(), socket);
191
+ return false;
192
+ }
193
+
194
+ if (event.name == "_remove_session_storage") {
195
+ sessionStorage.removeItem(event.payload.key);
158
196
  queueEvents(initialEvents(), socket);
159
197
  return false;
160
198
  }
@@ -249,7 +287,7 @@ export const applyRestEvent = async (event, socket) => {
249
287
  let eventSent = false;
250
288
  if (event.handler === "uploadFiles") {
251
289
 
252
- if (event.payload.files === undefined || event.payload.files.length === 0){
290
+ if (event.payload.files === undefined || event.payload.files.length === 0) {
253
291
  // Submit the event over the websocket to trigger the event handler.
254
292
  return await applyEvent(Event(event.name), socket)
255
293
  }
@@ -282,8 +320,8 @@ export const queueEvents = async (events, socket) => {
282
320
  * @param socket The socket object to send the event on.
283
321
  */
284
322
  export const processEvent = async (socket) => {
285
- // Only proceed if the socket is up, otherwise we throw the event into the void
286
- if (!socket) {
323
+ // Only proceed if the socket is up and no event in the queue uses state, otherwise we throw the event into the void
324
+ if (!socket && isStateful()) {
287
325
  return;
288
326
  }
289
327
 
@@ -350,9 +388,17 @@ export const connect = async (
350
388
  }
351
389
  }
352
390
 
391
+ const pagehideHandler = (event) => {
392
+ if (event.persisted && socket.current?.connected) {
393
+ console.log("Disconnect backend before bfcache on navigation");
394
+ socket.current.disconnect();
395
+ }
396
+ }
397
+
353
398
  // Once the socket is open, hydrate the page.
354
399
  socket.current.on("connect", () => {
355
400
  setConnectErrors([]);
401
+ window.addEventListener("pagehide", pagehideHandler);
356
402
  });
357
403
 
358
404
  socket.current.on("connect_error", (error) => {
@@ -362,6 +408,7 @@ export const connect = async (
362
408
  // When the socket disconnects reset the event_processing flag
363
409
  socket.current.on("disconnect", () => {
364
410
  event_processing = false;
411
+ window.removeEventListener("pagehide", pagehideHandler);
365
412
  });
366
413
 
367
414
  // On each received message, queue the updates and events.
@@ -512,7 +559,18 @@ export const hydrateClientStorage = (client_storage) => {
512
559
  }
513
560
  }
514
561
  }
515
- if (client_storage.cookies || client_storage.local_storage) {
562
+ if (client_storage.session_storage && typeof window != "undefined") {
563
+ for (const state_key in client_storage.session_storage) {
564
+ const session_options = client_storage.session_storage[state_key];
565
+ const session_storage_value = sessionStorage.getItem(
566
+ session_options.name || state_key
567
+ );
568
+ if (session_storage_value != null) {
569
+ client_storage_values[state_key] = session_storage_value;
570
+ }
571
+ }
572
+ }
573
+ if (client_storage.cookies || client_storage.local_storage || client_storage.session_storage) {
516
574
  return client_storage_values;
517
575
  }
518
576
  return {};
@@ -552,7 +610,15 @@ const applyClientStorageDelta = (client_storage, delta) => {
552
610
  ) {
553
611
  const options = client_storage.local_storage[state_key];
554
612
  localStorage.setItem(options.name || state_key, delta[substate][key]);
613
+ } else if(
614
+ client_storage.session_storage &&
615
+ state_key in client_storage.session_storage &&
616
+ typeof window !== "undefined"
617
+ ) {
618
+ const session_options = client_storage.session_storage[state_key];
619
+ sessionStorage.setItem(session_options.name || state_key, delta[substate][key]);
555
620
  }
621
+
556
622
  }
557
623
  }
558
624
  };
@@ -684,7 +750,7 @@ export const useEventLoop = (
684
750
  const change_start = () => {
685
751
  const main_state_dispatch = dispatch["state"]
686
752
  if (main_state_dispatch !== undefined) {
687
- main_state_dispatch({is_hydrated: false})
753
+ main_state_dispatch({ is_hydrated: false })
688
754
  }
689
755
  }
690
756
  const change_complete = () => addEvents(onLoadInternalEvent());
reflex/__init__.py CHANGED
@@ -287,12 +287,14 @@ _MAPPING: dict = {
287
287
  "background",
288
288
  "call_script",
289
289
  "clear_local_storage",
290
+ "clear_session_storage",
290
291
  "console_log",
291
292
  "download",
292
293
  "prevent_default",
293
294
  "redirect",
294
295
  "remove_cookie",
295
296
  "remove_local_storage",
297
+ "remove_session_storage",
296
298
  "set_clipboard",
297
299
  "set_focus",
298
300
  "scroll_to",
@@ -307,6 +309,7 @@ _MAPPING: dict = {
307
309
  "var",
308
310
  "Cookie",
309
311
  "LocalStorage",
312
+ "SessionStorage",
310
313
  "ComponentState",
311
314
  "State",
312
315
  ],
reflex/__init__.pyi CHANGED
@@ -157,12 +157,14 @@ from .event import EventHandler as EventHandler
157
157
  from .event import background as background
158
158
  from .event import call_script as call_script
159
159
  from .event import clear_local_storage as clear_local_storage
160
+ from .event import clear_session_storage as clear_session_storage
160
161
  from .event import console_log as console_log
161
162
  from .event import download as download
162
163
  from .event import prevent_default as prevent_default
163
164
  from .event import redirect as redirect
164
165
  from .event import remove_cookie as remove_cookie
165
166
  from .event import remove_local_storage as remove_local_storage
167
+ from .event import remove_session_storage as remove_session_storage
166
168
  from .event import set_clipboard as set_clipboard
167
169
  from .event import set_focus as set_focus
168
170
  from .event import scroll_to as scroll_to
@@ -177,6 +179,7 @@ from .model import Model as Model
177
179
  from .state import var as var
178
180
  from .state import Cookie as Cookie
179
181
  from .state import LocalStorage as LocalStorage
182
+ from .state import SessionStorage as SessionStorage
180
183
  from .state import ComponentState as ComponentState
181
184
  from .state import State as State
182
185
  from .style import Style as Style
reflex/admin.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """The Reflex Admin Dashboard."""
2
+
2
3
  from dataclasses import dataclass, field
3
4
  from typing import Optional
4
5
 
reflex/app.py CHANGED
@@ -176,9 +176,9 @@ class App(LifespanMixin, Base):
176
176
  stylesheets: List[str] = []
177
177
 
178
178
  # A component that is present on every page (defaults to the Connection Error banner).
179
- overlay_component: Optional[
180
- Union[Component, ComponentCallable]
181
- ] = default_overlay_component
179
+ overlay_component: Optional[Union[Component, ComponentCallable]] = (
180
+ default_overlay_component
181
+ )
182
182
 
183
183
  # Components to add to the head of every page.
184
184
  head_components: List[Component] = []
@@ -541,7 +541,7 @@ class App(LifespanMixin, Base):
541
541
 
542
542
  # Ensure state is enabled if this page uses state.
543
543
  if self.state is None:
544
- if on_load or component._has_event_triggers():
544
+ if on_load or component._has_stateful_event_triggers():
545
545
  self._enable_state()
546
546
  else:
547
547
  for var in component._get_vars(include_children=True):
@@ -751,10 +751,12 @@ class App(LifespanMixin, Base):
751
751
  if should_skip_compile():
752
752
  return False
753
753
 
754
+ nocompile = prerequisites.get_web_dir() / constants.NOCOMPILE_FILE
755
+
754
756
  # Check the nocompile file.
755
- if os.path.exists(constants.NOCOMPILE_FILE):
757
+ if nocompile.exists():
756
758
  # Delete the nocompile file
757
- os.remove(constants.NOCOMPILE_FILE)
759
+ nocompile.unlink()
758
760
  return False
759
761
 
760
762
  # By default, compile the app.
@@ -1,6 +1,7 @@
1
1
  """Shims the real reflex app module for running backend server (uvicorn or gunicorn).
2
2
  Only the app attribute is explicitly exposed.
3
3
  """
4
+
4
5
  from concurrent.futures import ThreadPoolExecutor
5
6
 
6
7
  from reflex import constants
@@ -8,7 +9,7 @@ from reflex.utils import telemetry
8
9
  from reflex.utils.exec import is_prod_mode
9
10
  from reflex.utils.prerequisites import get_app
10
11
 
11
- if "app" != constants.CompileVars.APP:
12
+ if constants.CompileVars.APP != "app":
12
13
  raise AssertionError("unexpected variable name for 'app'")
13
14
 
14
15
  telemetry.send("compile")
reflex/base.py CHANGED
@@ -6,12 +6,12 @@ import os
6
6
  from typing import TYPE_CHECKING, Any, List, Type
7
7
 
8
8
  try:
9
- import pydantic.v1 as pydantic
9
+ import pydantic.v1.main as pydantic_main
10
10
  from pydantic.v1 import BaseModel
11
11
  from pydantic.v1.fields import ModelField
12
12
  except ModuleNotFoundError:
13
13
  if not TYPE_CHECKING:
14
- import pydantic
14
+ import pydantic.main as pydantic_main
15
15
  from pydantic import BaseModel
16
16
  from pydantic.fields import ModelField # type: ignore
17
17
 
@@ -45,10 +45,10 @@ def validate_field_name(bases: List[Type["BaseModel"]], field_name: str) -> None
45
45
 
46
46
  # monkeypatch pydantic validate_field_name method to skip validating
47
47
  # shadowed state vars when reloading app via utils.prerequisites.get_app(reload=True)
48
- pydantic.main.validate_field_name = validate_field_name # type: ignore
48
+ pydantic_main.validate_field_name = validate_field_name # type: ignore
49
49
 
50
50
 
51
- class Base(pydantic.BaseModel): # pyright: ignore [reportUnboundVariable]
51
+ class Base(BaseModel): # pyright: ignore [reportUnboundVariable]
52
52
  """The base class subclassed by all Reflex classes.
53
53
 
54
54
  This class wraps Pydantic and provides common methods such as
@@ -20,6 +20,7 @@ from reflex.state import BaseState
20
20
  from reflex.style import SYSTEM_COLOR_MODE
21
21
  from reflex.utils.exec import is_prod_mode
22
22
  from reflex.utils.imports import ImportVar
23
+ from reflex.utils.prerequisites import get_web_dir
23
24
  from reflex.vars import Var
24
25
 
25
26
 
@@ -469,7 +470,7 @@ def compile_tailwind(
469
470
  The compiled Tailwind config.
470
471
  """
471
472
  # Get the path for the output file.
472
- output_path = constants.Tailwind.CONFIG
473
+ output_path = get_web_dir() / constants.Tailwind.CONFIG
473
474
 
474
475
  # Compile the config.
475
476
  code = _compile_tailwind(config)
@@ -483,7 +484,7 @@ def remove_tailwind_from_postcss() -> tuple[str, str]:
483
484
  The path and code of the compiled postcss.config.js.
484
485
  """
485
486
  # Get the path for the output file.
486
- output_path = constants.Dirs.POSTCSS_JS
487
+ output_path = str(get_web_dir() / constants.Dirs.POSTCSS_JS)
487
488
 
488
489
  code = [
489
490
  line
@@ -502,7 +503,7 @@ def purge_web_pages_dir():
502
503
  return
503
504
 
504
505
  # Empty out the web pages directory.
505
- utils.empty_dir(constants.Dirs.WEB_PAGES, keep_files=["_app.js"])
506
+ utils.empty_dir(get_web_dir() / constants.Dirs.PAGES, keep_files=["_app.js"])
506
507
 
507
508
 
508
509
  class ExecutorSafeFunctions:
@@ -37,7 +37,9 @@ class ReflexJinjaEnvironment(Environment):
37
37
  constants.CompileVars.PROCESSING: False,
38
38
  },
39
39
  "color_mode": constants.ColorMode.NAME,
40
+ "resolved_color_mode": constants.ColorMode.RESOLVED_NAME,
40
41
  "toggle_color_mode": constants.ColorMode.TOGGLE,
42
+ "set_color_mode": constants.ColorMode.SET,
41
43
  "use_color_mode": constants.ColorMode.USE,
42
44
  "hydrate": constants.CompileVars.HYDRATE,
43
45
  "on_load_internal": constants.CompileVars.ON_LOAD_INTERNAL,