reflex 0.7.14a6__py3-none-any.whl → 0.8.0a2__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/jinja/app/rxconfig.py.jinja2 +4 -1
- reflex/.templates/jinja/web/package.json.jinja2 +1 -1
- reflex/.templates/jinja/web/pages/_app.js.jinja2 +16 -10
- reflex/.templates/jinja/web/pages/_document.js.jinja2 +1 -1
- reflex/.templates/jinja/web/pages/base_page.js.jinja2 +0 -1
- reflex/.templates/jinja/web/pages/stateful_component.js.jinja2 +4 -0
- reflex/.templates/jinja/web/utils/context.js.jinja2 +25 -8
- reflex/.templates/web/app/entry.client.js +8 -0
- reflex/.templates/web/app/routes.js +10 -0
- reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +12 -37
- reflex/.templates/web/postcss.config.js +1 -1
- reflex/.templates/web/react-router.config.js +6 -0
- reflex/.templates/web/utils/client_side_routing.js +21 -19
- reflex/.templates/web/utils/react-theme.js +92 -0
- reflex/.templates/web/utils/state.js +160 -67
- reflex/.templates/web/vite.config.js +32 -0
- reflex/__init__.py +1 -6
- reflex/__init__.pyi +0 -4
- reflex/app.py +53 -116
- reflex/base.py +1 -87
- reflex/compiler/compiler.py +41 -8
- reflex/compiler/templates.py +3 -3
- reflex/compiler/utils.py +73 -33
- reflex/components/__init__.py +0 -2
- reflex/components/__init__.pyi +0 -3
- reflex/components/base/__init__.py +1 -5
- reflex/components/base/__init__.pyi +4 -6
- reflex/components/base/app_wrap.pyi +5 -4
- reflex/components/base/body.pyi +5 -4
- reflex/components/base/document.py +18 -14
- reflex/components/base/document.pyi +83 -27
- reflex/components/base/error_boundary.pyi +5 -4
- reflex/components/base/fragment.pyi +5 -4
- reflex/components/base/link.pyi +9 -7
- reflex/components/base/meta.pyi +17 -13
- reflex/components/base/script.py +60 -58
- reflex/components/base/script.pyi +246 -31
- reflex/components/base/strict_mode.pyi +5 -4
- reflex/components/component.py +146 -217
- reflex/components/core/__init__.py +1 -0
- reflex/components/core/__init__.pyi +1 -0
- reflex/components/core/auto_scroll.pyi +5 -4
- reflex/components/core/banner.pyi +25 -19
- reflex/components/core/client_side_routing.py +7 -6
- reflex/components/core/client_side_routing.pyi +6 -56
- reflex/components/core/clipboard.pyi +5 -4
- reflex/components/core/debounce.py +1 -0
- reflex/components/core/debounce.pyi +5 -4
- reflex/components/core/foreach.py +3 -2
- reflex/components/core/helmet.py +14 -0
- reflex/components/{next/base.pyi → core/helmet.pyi} +10 -7
- reflex/components/core/html.pyi +5 -4
- reflex/components/core/sticky.pyi +17 -13
- reflex/components/core/upload.py +2 -1
- reflex/components/core/upload.pyi +21 -16
- reflex/components/datadisplay/code.py +2 -72
- reflex/components/datadisplay/code.pyi +9 -10
- reflex/components/datadisplay/dataeditor.pyi +11 -6
- reflex/components/datadisplay/shiki_code_block.pyi +13 -10
- reflex/components/dynamic.py +5 -5
- reflex/components/el/element.pyi +5 -4
- reflex/components/el/elements/base.pyi +5 -4
- reflex/components/el/elements/forms.pyi +69 -52
- reflex/components/el/elements/inline.pyi +113 -85
- reflex/components/el/elements/media.pyi +105 -79
- reflex/components/el/elements/metadata.pyi +25 -19
- reflex/components/el/elements/other.pyi +29 -22
- reflex/components/el/elements/scripts.pyi +13 -10
- reflex/components/el/elements/sectioning.pyi +61 -46
- reflex/components/el/elements/tables.pyi +41 -31
- reflex/components/el/elements/typography.pyi +61 -46
- reflex/components/field.py +175 -0
- reflex/components/gridjs/datatable.py +2 -2
- reflex/components/gridjs/datatable.pyi +11 -9
- reflex/components/lucide/icon.py +6 -2
- reflex/components/lucide/icon.pyi +15 -10
- reflex/components/markdown/markdown.pyi +5 -4
- reflex/components/moment/moment.pyi +5 -4
- reflex/components/plotly/plotly.pyi +19 -10
- reflex/components/props.py +376 -27
- reflex/components/radix/primitives/accordion.py +8 -1
- reflex/components/radix/primitives/accordion.pyi +29 -22
- reflex/components/radix/primitives/base.pyi +9 -7
- reflex/components/radix/primitives/drawer.pyi +45 -34
- reflex/components/radix/primitives/form.pyi +41 -31
- reflex/components/radix/primitives/progress.pyi +21 -16
- reflex/components/radix/primitives/slider.pyi +21 -16
- reflex/components/radix/themes/base.py +3 -3
- reflex/components/radix/themes/base.pyi +33 -25
- reflex/components/radix/themes/color_mode.pyi +13 -10
- reflex/components/radix/themes/components/alert_dialog.pyi +29 -22
- reflex/components/radix/themes/components/aspect_ratio.pyi +5 -4
- reflex/components/radix/themes/components/avatar.pyi +5 -4
- reflex/components/radix/themes/components/badge.pyi +5 -4
- reflex/components/radix/themes/components/button.pyi +5 -4
- reflex/components/radix/themes/components/callout.pyi +21 -16
- reflex/components/radix/themes/components/card.pyi +5 -4
- reflex/components/radix/themes/components/checkbox.pyi +13 -10
- reflex/components/radix/themes/components/checkbox_cards.pyi +9 -7
- reflex/components/radix/themes/components/checkbox_group.pyi +9 -7
- reflex/components/radix/themes/components/context_menu.pyi +53 -40
- reflex/components/radix/themes/components/data_list.pyi +17 -13
- reflex/components/radix/themes/components/dialog.pyi +29 -22
- reflex/components/radix/themes/components/dropdown_menu.pyi +33 -25
- reflex/components/radix/themes/components/hover_card.pyi +17 -13
- reflex/components/radix/themes/components/icon_button.pyi +5 -4
- reflex/components/radix/themes/components/inset.pyi +5 -4
- reflex/components/radix/themes/components/popover.pyi +17 -13
- reflex/components/radix/themes/components/progress.pyi +5 -4
- reflex/components/radix/themes/components/radio.pyi +5 -4
- reflex/components/radix/themes/components/radio_cards.pyi +9 -7
- reflex/components/radix/themes/components/radio_group.pyi +17 -13
- reflex/components/radix/themes/components/scroll_area.pyi +5 -4
- reflex/components/radix/themes/components/segmented_control.pyi +9 -7
- reflex/components/radix/themes/components/select.pyi +37 -28
- reflex/components/radix/themes/components/separator.pyi +5 -4
- reflex/components/radix/themes/components/skeleton.pyi +5 -4
- reflex/components/radix/themes/components/slider.pyi +5 -4
- reflex/components/radix/themes/components/spinner.pyi +5 -4
- reflex/components/radix/themes/components/switch.pyi +5 -4
- reflex/components/radix/themes/components/table.pyi +29 -22
- reflex/components/radix/themes/components/tabs.pyi +21 -16
- reflex/components/radix/themes/components/text_area.pyi +5 -4
- reflex/components/radix/themes/components/text_field.pyi +13 -10
- reflex/components/radix/themes/components/tooltip.pyi +5 -4
- reflex/components/radix/themes/layout/base.pyi +5 -4
- reflex/components/radix/themes/layout/box.pyi +5 -4
- reflex/components/radix/themes/layout/center.pyi +5 -4
- reflex/components/radix/themes/layout/container.pyi +5 -4
- reflex/components/radix/themes/layout/flex.pyi +5 -4
- reflex/components/radix/themes/layout/grid.pyi +5 -4
- reflex/components/radix/themes/layout/list.pyi +21 -16
- reflex/components/radix/themes/layout/section.pyi +5 -4
- reflex/components/radix/themes/layout/spacer.pyi +5 -4
- reflex/components/radix/themes/layout/stack.pyi +13 -10
- reflex/components/radix/themes/typography/blockquote.pyi +5 -4
- reflex/components/radix/themes/typography/code.pyi +5 -4
- reflex/components/radix/themes/typography/heading.pyi +5 -4
- reflex/components/radix/themes/typography/link.py +46 -11
- reflex/components/radix/themes/typography/link.pyi +311 -6
- reflex/components/radix/themes/typography/text.pyi +29 -22
- reflex/components/react_player/audio.pyi +5 -4
- reflex/components/react_player/react_player.pyi +5 -4
- reflex/components/react_player/video.pyi +5 -4
- reflex/components/recharts/cartesian.py +2 -1
- reflex/components/recharts/cartesian.pyi +65 -46
- reflex/components/recharts/charts.py +4 -2
- reflex/components/recharts/charts.pyi +36 -24
- reflex/components/recharts/general.pyi +24 -18
- reflex/components/recharts/polar.py +8 -4
- reflex/components/recharts/polar.pyi +16 -10
- reflex/components/recharts/recharts.pyi +9 -7
- reflex/components/sonner/toast.py +2 -2
- reflex/components/sonner/toast.pyi +10 -8
- reflex/config.py +3 -77
- reflex/constants/__init__.py +2 -2
- reflex/constants/base.py +28 -11
- reflex/constants/compiler.py +5 -3
- reflex/constants/event.py +1 -0
- reflex/constants/installer.py +22 -16
- reflex/constants/route.py +19 -7
- reflex/constants/state.py +2 -0
- reflex/custom_components/custom_components.py +0 -14
- reflex/environment.py +1 -1
- reflex/event.py +178 -81
- reflex/experimental/__init__.py +0 -30
- reflex/istate/proxy.py +5 -3
- reflex/page.py +0 -27
- reflex/plugins/__init__.py +3 -2
- reflex/plugins/base.py +5 -1
- reflex/plugins/shared_tailwind.py +158 -0
- reflex/plugins/sitemap.py +206 -0
- reflex/plugins/tailwind_v3.py +13 -106
- reflex/plugins/tailwind_v4.py +15 -108
- reflex/reflex.py +1 -0
- reflex/route.py +15 -21
- reflex/state.py +134 -140
- reflex/testing.py +58 -10
- reflex/utils/build.py +38 -82
- reflex/utils/exec.py +59 -161
- reflex/utils/export.py +2 -2
- reflex/utils/imports.py +0 -4
- reflex/utils/misc.py +28 -0
- reflex/utils/prerequisites.py +65 -62
- reflex/utils/processes.py +8 -7
- reflex/utils/pyi_generator.py +21 -9
- reflex/utils/serializers.py +14 -1
- reflex/utils/types.py +196 -61
- reflex/vars/__init__.py +2 -0
- reflex/vars/base.py +367 -134
- {reflex-0.7.14a6.dist-info → reflex-0.8.0a2.dist-info}/METADATA +12 -5
- {reflex-0.7.14a6.dist-info → reflex-0.8.0a2.dist-info}/RECORD +195 -202
- reflex/.templates/web/next.config.js +0 -7
- reflex/components/base/head.py +0 -20
- reflex/components/base/head.pyi +0 -116
- reflex/components/next/__init__.py +0 -10
- reflex/components/next/base.py +0 -7
- reflex/components/next/image.py +0 -117
- reflex/components/next/image.pyi +0 -94
- reflex/components/next/link.py +0 -20
- reflex/components/next/link.pyi +0 -67
- reflex/components/next/video.py +0 -38
- reflex/components/next/video.pyi +0 -68
- reflex/components/suneditor/__init__.py +0 -5
- reflex/components/suneditor/editor.py +0 -269
- reflex/components/suneditor/editor.pyi +0 -199
- reflex/experimental/layout.py +0 -254
- reflex/experimental/layout.pyi +0 -814
- {reflex-0.7.14a6.dist-info → reflex-0.8.0a2.dist-info}/WHEEL +0 -0
- {reflex-0.7.14a6.dist-info → reflex-0.8.0a2.dist-info}/entry_points.txt +0 -0
- {reflex-0.7.14a6.dist-info → reflex-0.8.0a2.dist-info}/licenses/LICENSE +0 -0
|
@@ -6,14 +6,19 @@ import env from "$/env.json";
|
|
|
6
6
|
import reflexEnvironment from "$/reflex.json";
|
|
7
7
|
import Cookies from "universal-cookie";
|
|
8
8
|
import { useEffect, useRef, useState } from "react";
|
|
9
|
-
import
|
|
9
|
+
import {
|
|
10
|
+
useLocation,
|
|
11
|
+
useNavigate,
|
|
12
|
+
useSearchParams,
|
|
13
|
+
useParams,
|
|
14
|
+
} from "react-router";
|
|
10
15
|
import {
|
|
11
16
|
initialEvents,
|
|
12
17
|
initialState,
|
|
13
18
|
onLoadInternalEvent,
|
|
14
19
|
state_name,
|
|
15
20
|
exception_state_name,
|
|
16
|
-
} from "$/utils/context
|
|
21
|
+
} from "$/utils/context";
|
|
17
22
|
import debounce from "$/utils/helpers/debounce";
|
|
18
23
|
import throttle from "$/utils/helpers/throttle";
|
|
19
24
|
|
|
@@ -158,24 +163,48 @@ export const evalReactComponent = async (component) => {
|
|
|
158
163
|
* Only Queue and process events when websocket connection exists.
|
|
159
164
|
* @param event The event to queue.
|
|
160
165
|
* @param socket The socket object to send the event on.
|
|
166
|
+
* @param navigate The navigate function from React Router
|
|
167
|
+
* @param params The params object from React Router
|
|
161
168
|
*
|
|
162
169
|
* @returns Adds event to queue and processes it if websocket exits, does nothing otherwise.
|
|
163
170
|
*/
|
|
164
|
-
export const queueEventIfSocketExists = async (
|
|
171
|
+
export const queueEventIfSocketExists = async (
|
|
172
|
+
events,
|
|
173
|
+
socket,
|
|
174
|
+
navigate,
|
|
175
|
+
params,
|
|
176
|
+
) => {
|
|
165
177
|
if (!socket) {
|
|
166
178
|
return;
|
|
167
179
|
}
|
|
168
|
-
await queueEvents(events, socket);
|
|
180
|
+
await queueEvents(events, socket, navigate, params);
|
|
169
181
|
};
|
|
170
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Check if a string is a valid HTTP URL.
|
|
185
|
+
* @param string The string to check.
|
|
186
|
+
*
|
|
187
|
+
* @returns The URL object if valid, undefined otherwise.
|
|
188
|
+
* */
|
|
189
|
+
function urlFrom(string) {
|
|
190
|
+
try {
|
|
191
|
+
return new URL(string);
|
|
192
|
+
} catch {
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
197
|
+
|
|
171
198
|
/**
|
|
172
199
|
* Handle frontend event or send the event to the backend via Websocket.
|
|
173
200
|
* @param event The event to send.
|
|
174
201
|
* @param socket The socket object to send the event on.
|
|
202
|
+
* @param navigate The navigate function from useNavigate
|
|
203
|
+
* @param params The params object from useParams
|
|
175
204
|
*
|
|
176
205
|
* @returns True if the event was sent, false if it was handled locally.
|
|
177
206
|
*/
|
|
178
|
-
export const applyEvent = async (event, socket) => {
|
|
207
|
+
export const applyEvent = async (event, socket, navigate, params) => {
|
|
179
208
|
// Handle special events
|
|
180
209
|
if (event.name == "_redirect") {
|
|
181
210
|
if ((event.payload.path ?? undefined) === undefined) {
|
|
@@ -183,41 +212,54 @@ export const applyEvent = async (event, socket) => {
|
|
|
183
212
|
}
|
|
184
213
|
if (event.payload.external) {
|
|
185
214
|
window.open(event.payload.path, "_blank", "noopener");
|
|
186
|
-
|
|
187
|
-
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
const url = urlFrom(event.payload.path);
|
|
218
|
+
let pathname = event.payload.path;
|
|
219
|
+
if (url) {
|
|
220
|
+
if (url.host !== window.location.host) {
|
|
221
|
+
// External URL
|
|
222
|
+
window.location.assign(event.payload.path);
|
|
223
|
+
return false;
|
|
224
|
+
} else {
|
|
225
|
+
pathname = url.pathname + url.search + url.hash;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (event.payload.replace) {
|
|
229
|
+
navigate(pathname, { replace: true });
|
|
188
230
|
} else {
|
|
189
|
-
|
|
231
|
+
navigate(pathname);
|
|
190
232
|
}
|
|
191
233
|
return false;
|
|
192
234
|
}
|
|
193
235
|
|
|
194
236
|
if (event.name == "_remove_cookie") {
|
|
195
237
|
cookies.remove(event.payload.key, { ...event.payload.options });
|
|
196
|
-
queueEventIfSocketExists(initialEvents(), socket);
|
|
238
|
+
queueEventIfSocketExists(initialEvents(), socket, navigate, params);
|
|
197
239
|
return false;
|
|
198
240
|
}
|
|
199
241
|
|
|
200
242
|
if (event.name == "_clear_local_storage") {
|
|
201
243
|
localStorage.clear();
|
|
202
|
-
queueEventIfSocketExists(initialEvents(), socket);
|
|
244
|
+
queueEventIfSocketExists(initialEvents(), socket, navigate, params);
|
|
203
245
|
return false;
|
|
204
246
|
}
|
|
205
247
|
|
|
206
248
|
if (event.name == "_remove_local_storage") {
|
|
207
249
|
localStorage.removeItem(event.payload.key);
|
|
208
|
-
queueEventIfSocketExists(initialEvents(), socket);
|
|
250
|
+
queueEventIfSocketExists(initialEvents(), socket, navigate, params);
|
|
209
251
|
return false;
|
|
210
252
|
}
|
|
211
253
|
|
|
212
254
|
if (event.name == "_clear_session_storage") {
|
|
213
255
|
sessionStorage.clear();
|
|
214
|
-
queueEvents(initialEvents(), socket);
|
|
256
|
+
queueEvents(initialEvents(), socket, navigate, params);
|
|
215
257
|
return false;
|
|
216
258
|
}
|
|
217
259
|
|
|
218
260
|
if (event.name == "_remove_session_storage") {
|
|
219
261
|
sessionStorage.removeItem(event.payload.key);
|
|
220
|
-
queueEvents(initialEvents(), socket);
|
|
262
|
+
queueEvents(initialEvents(), socket, navigate, params);
|
|
221
263
|
return false;
|
|
222
264
|
}
|
|
223
265
|
|
|
@@ -322,11 +364,15 @@ export const applyEvent = async (event, socket) => {
|
|
|
322
364
|
event.router_data === undefined ||
|
|
323
365
|
Object.keys(event.router_data).length === 0
|
|
324
366
|
) {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
367
|
+
// Since we don't have router directly, we need to get info from our hooks
|
|
368
|
+
event.router_data = {
|
|
369
|
+
pathname: window.location.pathname,
|
|
370
|
+
query: {
|
|
371
|
+
...Object.fromEntries(new URLSearchParams(window.location.search)),
|
|
372
|
+
...params(),
|
|
373
|
+
},
|
|
374
|
+
asPath: window.location.pathname + window.location.search,
|
|
375
|
+
};
|
|
330
376
|
}
|
|
331
377
|
|
|
332
378
|
// Send the event to the server.
|
|
@@ -342,15 +388,22 @@ export const applyEvent = async (event, socket) => {
|
|
|
342
388
|
* Send an event to the server via REST.
|
|
343
389
|
* @param event The current event.
|
|
344
390
|
* @param socket The socket object to send the response event(s) on.
|
|
391
|
+
* @param navigate The navigate function from React Router
|
|
392
|
+
* @param params The params object from React Router
|
|
345
393
|
*
|
|
346
394
|
* @returns Whether the event was sent.
|
|
347
395
|
*/
|
|
348
|
-
export const applyRestEvent = async (event, socket) => {
|
|
396
|
+
export const applyRestEvent = async (event, socket, navigate, params) => {
|
|
349
397
|
let eventSent = false;
|
|
350
398
|
if (event.handler === "uploadFiles") {
|
|
351
399
|
if (event.payload.files === undefined || event.payload.files.length === 0) {
|
|
352
400
|
// Submit the event over the websocket to trigger the event handler.
|
|
353
|
-
return await applyEvent(
|
|
401
|
+
return await applyEvent(
|
|
402
|
+
Event(event.name, { files: [] }),
|
|
403
|
+
socket,
|
|
404
|
+
navigate,
|
|
405
|
+
params,
|
|
406
|
+
);
|
|
354
407
|
}
|
|
355
408
|
|
|
356
409
|
// Start upload, but do not wait for it, which would block other events.
|
|
@@ -371,8 +424,16 @@ export const applyRestEvent = async (event, socket) => {
|
|
|
371
424
|
* @param events Array of events to queue.
|
|
372
425
|
* @param socket The socket object to send the event on.
|
|
373
426
|
* @param prepend Whether to place the events at the beginning of the queue.
|
|
427
|
+
* @param navigate The navigate function from React Router
|
|
428
|
+
* @param params The params object from React Router
|
|
374
429
|
*/
|
|
375
|
-
export const queueEvents = async (
|
|
430
|
+
export const queueEvents = async (
|
|
431
|
+
events,
|
|
432
|
+
socket,
|
|
433
|
+
prepend,
|
|
434
|
+
navigate,
|
|
435
|
+
params,
|
|
436
|
+
) => {
|
|
376
437
|
if (prepend) {
|
|
377
438
|
// Drain the existing queue and place it after the given events.
|
|
378
439
|
events = [
|
|
@@ -383,14 +444,16 @@ export const queueEvents = async (events, socket, prepend) => {
|
|
|
383
444
|
];
|
|
384
445
|
}
|
|
385
446
|
event_queue.push(...events.filter((e) => e !== undefined && e !== null));
|
|
386
|
-
await processEvent(socket.current);
|
|
447
|
+
await processEvent(socket.current, navigate, params);
|
|
387
448
|
};
|
|
388
449
|
|
|
389
450
|
/**
|
|
390
451
|
* Process an event off the event queue.
|
|
391
452
|
* @param socket The socket object to send the event on.
|
|
453
|
+
* @param navigate The navigate function from React Router
|
|
454
|
+
* @param params The params object from React Router
|
|
392
455
|
*/
|
|
393
|
-
export const processEvent = async (socket) => {
|
|
456
|
+
export const processEvent = async (socket, navigate, params) => {
|
|
394
457
|
// Only proceed if the socket is up and no event in the queue uses state, otherwise we throw the event into the void
|
|
395
458
|
if (!socket && isStateful()) {
|
|
396
459
|
return;
|
|
@@ -410,16 +473,16 @@ export const processEvent = async (socket) => {
|
|
|
410
473
|
let eventSent = false;
|
|
411
474
|
// Process events with handlers via REST and all others via websockets.
|
|
412
475
|
if (event.handler) {
|
|
413
|
-
eventSent = await applyRestEvent(event, socket);
|
|
476
|
+
eventSent = await applyRestEvent(event, socket, navigate, params);
|
|
414
477
|
} else {
|
|
415
|
-
eventSent = await applyEvent(event, socket);
|
|
478
|
+
eventSent = await applyEvent(event, socket, navigate, params);
|
|
416
479
|
}
|
|
417
480
|
// If no event was sent, set processing to false.
|
|
418
481
|
if (!eventSent) {
|
|
419
482
|
event_processing = false;
|
|
420
483
|
// recursively call processEvent to drain the queue, since there is
|
|
421
484
|
// no state update to trigger the useEffect event loop.
|
|
422
|
-
await processEvent(socket);
|
|
485
|
+
await processEvent(socket, navigate, params);
|
|
423
486
|
}
|
|
424
487
|
};
|
|
425
488
|
|
|
@@ -430,6 +493,8 @@ export const processEvent = async (socket) => {
|
|
|
430
493
|
* @param transports The transports to use.
|
|
431
494
|
* @param setConnectErrors The function to update connection error value.
|
|
432
495
|
* @param client_storage The client storage object from context.js
|
|
496
|
+
* @param navigate The navigate function from React Router
|
|
497
|
+
* @param params The params object from React Router
|
|
433
498
|
*/
|
|
434
499
|
export const connect = async (
|
|
435
500
|
socket,
|
|
@@ -437,6 +502,8 @@ export const connect = async (
|
|
|
437
502
|
transports,
|
|
438
503
|
setConnectErrors,
|
|
439
504
|
client_storage = {},
|
|
505
|
+
navigate,
|
|
506
|
+
params,
|
|
440
507
|
) => {
|
|
441
508
|
// Get backend URL object from the endpoint.
|
|
442
509
|
const endpoint = getBackendURL(EVENTURL);
|
|
@@ -511,12 +578,12 @@ export const connect = async (
|
|
|
511
578
|
applyClientStorageDelta(client_storage, update.delta);
|
|
512
579
|
event_processing = !update.final;
|
|
513
580
|
if (update.events) {
|
|
514
|
-
queueEvents(update.events, socket);
|
|
581
|
+
queueEvents(update.events, socket, false, navigate, params);
|
|
515
582
|
}
|
|
516
583
|
});
|
|
517
584
|
socket.current.on("reload", async (event) => {
|
|
518
585
|
event_processing = false;
|
|
519
|
-
queueEvents([...initialEvents(), event], socket, true);
|
|
586
|
+
queueEvents([...initialEvents(), event], socket, true, navigate, params);
|
|
520
587
|
});
|
|
521
588
|
|
|
522
589
|
document.addEventListener("visibilitychange", checkVisibility);
|
|
@@ -710,7 +777,10 @@ const applyClientStorageDelta = (client_storage, delta) => {
|
|
|
710
777
|
);
|
|
711
778
|
if (unqualified_states.length === 1) {
|
|
712
779
|
const main_state = delta[unqualified_states[0]];
|
|
713
|
-
if (
|
|
780
|
+
if (
|
|
781
|
+
main_state.is_hydrated_rx_state_ !== undefined &&
|
|
782
|
+
!main_state.is_hydrated_rx_state_
|
|
783
|
+
) {
|
|
714
784
|
// skip if the state is not hydrated yet, since all client storage
|
|
715
785
|
// values are sent in the hydrate event
|
|
716
786
|
return;
|
|
@@ -748,7 +818,7 @@ const applyClientStorageDelta = (client_storage, delta) => {
|
|
|
748
818
|
};
|
|
749
819
|
|
|
750
820
|
/**
|
|
751
|
-
* Establish websocket event loop for a
|
|
821
|
+
* Establish websocket event loop for a React Router page.
|
|
752
822
|
* @param dispatch The reducer dispatch function to update state.
|
|
753
823
|
* @param initial_events The initial app events.
|
|
754
824
|
* @param client_storage The client storage object from context.js
|
|
@@ -763,8 +833,22 @@ export const useEventLoop = (
|
|
|
763
833
|
client_storage = {},
|
|
764
834
|
) => {
|
|
765
835
|
const socket = useRef(null);
|
|
766
|
-
const
|
|
836
|
+
const location = useLocation();
|
|
837
|
+
const navigate = useNavigate();
|
|
838
|
+
const paramsR = useParams();
|
|
839
|
+
const prevLocationRef = useRef(location);
|
|
840
|
+
const [searchParams] = useSearchParams();
|
|
767
841
|
const [connectErrors, setConnectErrors] = useState([]);
|
|
842
|
+
const params = useRef(paramsR);
|
|
843
|
+
|
|
844
|
+
useEffect(() => {
|
|
845
|
+
const { "*": splat, ...remainingParams } = paramsR;
|
|
846
|
+
if (splat) {
|
|
847
|
+
params.current = { ...remainingParams, splat: splat.split("/") };
|
|
848
|
+
} else {
|
|
849
|
+
params.current = remainingParams;
|
|
850
|
+
}
|
|
851
|
+
}, [paramsR]);
|
|
768
852
|
|
|
769
853
|
// Function to add new events to the event queue.
|
|
770
854
|
const addEvents = (events, args, event_actions) => {
|
|
@@ -803,32 +887,38 @@ export const useEventLoop = (
|
|
|
803
887
|
// If debounce is used, queue the events after some delay
|
|
804
888
|
debounce(
|
|
805
889
|
combined_name,
|
|
806
|
-
() =>
|
|
890
|
+
() =>
|
|
891
|
+
queueEvents(_events, socket, false, navigate, () => params.current),
|
|
807
892
|
event_actions.debounce,
|
|
808
893
|
);
|
|
809
894
|
} else {
|
|
810
|
-
queueEvents(_events, socket);
|
|
895
|
+
queueEvents(_events, socket, false, navigate, () => params.current);
|
|
811
896
|
}
|
|
812
897
|
};
|
|
813
898
|
|
|
814
899
|
const sentHydrate = useRef(false); // Avoid double-hydrate due to React strict-mode
|
|
815
900
|
useEffect(() => {
|
|
816
|
-
if (
|
|
901
|
+
if (!sentHydrate.current) {
|
|
817
902
|
queueEvents(
|
|
818
903
|
initial_events().map((e) => ({
|
|
819
904
|
...e,
|
|
820
|
-
router_data:
|
|
821
|
-
pathname,
|
|
822
|
-
query
|
|
823
|
-
|
|
824
|
-
|
|
905
|
+
router_data: {
|
|
906
|
+
pathname: location.pathname,
|
|
907
|
+
query: {
|
|
908
|
+
...Object.fromEntries(searchParams.entries()),
|
|
909
|
+
...params.current,
|
|
910
|
+
},
|
|
911
|
+
asPath: location.pathname + location.search,
|
|
912
|
+
},
|
|
825
913
|
})),
|
|
826
914
|
socket,
|
|
827
915
|
true,
|
|
916
|
+
navigate,
|
|
917
|
+
() => params.current,
|
|
828
918
|
);
|
|
829
919
|
sentHydrate.current = true;
|
|
830
920
|
}
|
|
831
|
-
}, [
|
|
921
|
+
}, []);
|
|
832
922
|
|
|
833
923
|
// Handle frontend errors and send them to the backend via websocket.
|
|
834
924
|
useEffect(() => {
|
|
@@ -871,6 +961,8 @@ export const useEventLoop = (
|
|
|
871
961
|
["websocket"],
|
|
872
962
|
setConnectErrors,
|
|
873
963
|
client_storage,
|
|
964
|
+
navigate,
|
|
965
|
+
() => params.current,
|
|
874
966
|
);
|
|
875
967
|
}
|
|
876
968
|
}
|
|
@@ -885,14 +977,14 @@ export const useEventLoop = (
|
|
|
885
977
|
|
|
886
978
|
// Main event loop.
|
|
887
979
|
useEffect(() => {
|
|
888
|
-
// Skip if the
|
|
889
|
-
if (
|
|
980
|
+
// Skip if the backend is disabled
|
|
981
|
+
if (isBackendDisabled()) {
|
|
890
982
|
return;
|
|
891
983
|
}
|
|
892
984
|
(async () => {
|
|
893
985
|
// Process all outstanding events.
|
|
894
986
|
while (event_queue.length > 0 && !event_processing) {
|
|
895
|
-
await processEvent(socket.current);
|
|
987
|
+
await processEvent(socket.current, navigate, () => params.current);
|
|
896
988
|
}
|
|
897
989
|
})();
|
|
898
990
|
});
|
|
@@ -928,31 +1020,32 @@ export const useEventLoop = (
|
|
|
928
1020
|
return () => window.removeEventListener("storage", handleStorage);
|
|
929
1021
|
});
|
|
930
1022
|
|
|
931
|
-
// Route after the initial page hydration
|
|
1023
|
+
// Route after the initial page hydration
|
|
932
1024
|
useEffect(() => {
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
1025
|
+
// This will run when the location changes
|
|
1026
|
+
if (
|
|
1027
|
+
location.pathname + location.search + location.hash !==
|
|
1028
|
+
prevLocationRef.current.pathname +
|
|
1029
|
+
prevLocationRef.current.search +
|
|
1030
|
+
prevLocationRef.current.hash
|
|
1031
|
+
) {
|
|
1032
|
+
// Equivalent to routeChangeStart - runs when navigation begins
|
|
1033
|
+
const change_start = () => {
|
|
1034
|
+
const main_state_dispatch = dispatch["reflex___state____state"];
|
|
1035
|
+
if (main_state_dispatch !== undefined) {
|
|
1036
|
+
main_state_dispatch({ is_hydrated_rx_state_: false });
|
|
1037
|
+
}
|
|
1038
|
+
};
|
|
1039
|
+
change_start();
|
|
1040
|
+
|
|
1041
|
+
// Equivalent to routeChangeComplete - runs after navigation completes
|
|
1042
|
+
const change_complete = () => addEvents(onLoadInternalEvent());
|
|
1043
|
+
change_complete();
|
|
1044
|
+
|
|
1045
|
+
// Update the ref
|
|
1046
|
+
prevLocationRef.current = location;
|
|
1047
|
+
}
|
|
1048
|
+
}, [location, dispatch, onLoadInternalEvent, addEvents]);
|
|
956
1049
|
|
|
957
1050
|
return [addEvents, connectErrors];
|
|
958
1051
|
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { fileURLToPath, URL } from "url";
|
|
2
|
+
import { reactRouter } from "@react-router/dev/vite";
|
|
3
|
+
import { defineConfig } from "vite";
|
|
4
|
+
|
|
5
|
+
export default defineConfig((config) => ({
|
|
6
|
+
plugins: [reactRouter()],
|
|
7
|
+
build: {
|
|
8
|
+
rollupOptions: {
|
|
9
|
+
jsx: {},
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
server: {
|
|
13
|
+
port: process.env.PORT,
|
|
14
|
+
},
|
|
15
|
+
resolve: {
|
|
16
|
+
mainFields: ["browser", "module", "jsnext"],
|
|
17
|
+
alias: [
|
|
18
|
+
{
|
|
19
|
+
find: "$",
|
|
20
|
+
replacement: fileURLToPath(new URL("./", import.meta.url)),
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
find: "@",
|
|
24
|
+
replacement: fileURLToPath(new URL("./public", import.meta.url)),
|
|
25
|
+
},
|
|
26
|
+
].concat(
|
|
27
|
+
config.command === "build"
|
|
28
|
+
? [{ find: "react-dom/server", replacement: "react-dom/server.node" }]
|
|
29
|
+
: [],
|
|
30
|
+
),
|
|
31
|
+
},
|
|
32
|
+
}));
|
reflex/__init__.py
CHANGED
|
@@ -279,12 +279,7 @@ _MAPPING: dict = {
|
|
|
279
279
|
"components.el.elements.media": ["image"],
|
|
280
280
|
"components.lucide": ["icon"],
|
|
281
281
|
**COMPONENTS_BASE_MAPPING,
|
|
282
|
-
"components
|
|
283
|
-
"editor",
|
|
284
|
-
"EditorButtonList",
|
|
285
|
-
"EditorOptions",
|
|
286
|
-
],
|
|
287
|
-
"components": ["el", "radix", "lucide", "recharts", "next"],
|
|
282
|
+
"components": ["el", "radix", "lucide", "recharts"],
|
|
288
283
|
"components.markdown": ["markdown"],
|
|
289
284
|
**RADIX_MAPPING,
|
|
290
285
|
"components.plotly": ["plotly"],
|
reflex/__init__.pyi
CHANGED
|
@@ -24,7 +24,6 @@ from .assets import asset as asset
|
|
|
24
24
|
from .base import Base as Base
|
|
25
25
|
from .components import el as el
|
|
26
26
|
from .components import lucide as lucide
|
|
27
|
-
from .components import next as next
|
|
28
27
|
from .components import radix as radix
|
|
29
28
|
from .components import recharts as recharts
|
|
30
29
|
from .components.base.fragment import Fragment as Fragment
|
|
@@ -149,9 +148,6 @@ from .components.radix.themes.typography.text import text as text
|
|
|
149
148
|
from .components.react_player import audio as audio
|
|
150
149
|
from .components.react_player import video as video
|
|
151
150
|
from .components.sonner.toast import toast as toast
|
|
152
|
-
from .components.suneditor import EditorButtonList as EditorButtonList
|
|
153
|
-
from .components.suneditor import EditorOptions as EditorOptions
|
|
154
|
-
from .components.suneditor import editor as editor
|
|
155
151
|
from .config import Config as Config
|
|
156
152
|
from .config import DBConfig as DBConfig
|
|
157
153
|
from .constants import Env as Env
|