reflex 0.7.14a5__py3-none-any.whl → 0.8.0a1__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/utils/context.js.jinja2 +25 -6
- 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 -32
- 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 +89 -0
- reflex/.templates/web/utils/state.js +155 -67
- reflex/.templates/web/vite.config.js +32 -0
- reflex/__init__.py +1 -6
- reflex/__init__.pyi +0 -4
- reflex/app.py +69 -132
- reflex/base.py +1 -87
- reflex/compiler/compiler.py +40 -3
- reflex/compiler/utils.py +54 -28
- 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 +109 -194
- 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.pyi +9 -7
- reflex/components/datadisplay/dataeditor.pyi +5 -4
- reflex/components/datadisplay/shiki_code_block.pyi +13 -10
- reflex/components/dynamic.py +4 -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 +42 -9
- 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 +7 -6
- reflex/config.py +3 -77
- reflex/constants/__init__.py +1 -0
- reflex/constants/base.py +38 -8
- reflex/constants/compiler.py +4 -2
- reflex/constants/event.py +1 -0
- reflex/constants/installer.py +23 -16
- 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/state.py +134 -140
- reflex/testing.py +57 -9
- reflex/utils/build.py +9 -69
- reflex/utils/exec.py +59 -161
- reflex/utils/export.py +1 -1
- reflex/utils/imports.py +0 -4
- reflex/utils/misc.py +28 -0
- reflex/utils/prerequisites.py +62 -59
- reflex/utils/processes.py +6 -6
- 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.14a5.dist-info → reflex-0.8.0a1.dist-info}/METADATA +12 -5
- {reflex-0.7.14a5.dist-info → reflex-0.8.0a1.dist-info}/RECORD +190 -196
- 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-0.7.14a5.dist-info → reflex-0.8.0a1.dist-info}/WHEEL +0 -0
- {reflex-0.7.14a5.dist-info → reflex-0.8.0a1.dist-info}/entry_points.txt +0 -0
- {reflex-0.7.14a5.dist-info → reflex-0.8.0a1.dist-info}/licenses/LICENSE +0 -0
|
@@ -6,8 +6,10 @@ import '$/styles/__reflex_global_styles.css'
|
|
|
6
6
|
{% endblock %}
|
|
7
7
|
|
|
8
8
|
{% block declaration %}
|
|
9
|
-
import { EventLoopProvider, StateProvider, defaultColorMode } from "$/utils/context
|
|
10
|
-
import { ThemeProvider } from '
|
|
9
|
+
import { EventLoopProvider, StateProvider, defaultColorMode } from "$/utils/context";
|
|
10
|
+
import { ThemeProvider } from '$/utils/react-theme';
|
|
11
|
+
import { Layout as AppLayout } from './_document';
|
|
12
|
+
import { Outlet } from 'react-router';
|
|
11
13
|
{% for library_alias, library_path in window_libraries %}
|
|
12
14
|
import * as {{library_alias}} from "{{library_path}}";
|
|
13
15
|
{% endfor %}
|
|
@@ -26,8 +28,9 @@ function AppWrap({children}) {
|
|
|
26
28
|
)
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
|
|
32
|
+
export function Layout({children}) {
|
|
33
|
+
React.useEffect(() => {
|
|
31
34
|
// Make contexts and state objects available globally for dynamic eval'd components
|
|
32
35
|
let windowImports = {
|
|
33
36
|
{% for library_alias, library_path in window_libraries %}
|
|
@@ -36,17 +39,20 @@ export default function MyApp({ Component, pageProps }) {
|
|
|
36
39
|
};
|
|
37
40
|
window["__reflex"] = windowImports;
|
|
38
41
|
}, []);
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
|
|
43
|
+
return jsx(AppLayout, {},
|
|
44
|
+
jsx(ThemeProvider, {defaultTheme: defaultColorMode, attribute: "class"},
|
|
41
45
|
jsx(StateProvider, {},
|
|
42
|
-
jsx(EventLoopProvider, {},
|
|
43
|
-
jsx(AppWrap, {},
|
|
44
|
-
jsx(Component, pageProps)
|
|
45
|
-
)
|
|
46
|
+
jsx(EventLoopProvider, {},
|
|
47
|
+
jsx(AppWrap, {}, children)
|
|
46
48
|
)
|
|
47
49
|
)
|
|
48
50
|
)
|
|
49
51
|
);
|
|
50
52
|
}
|
|
51
53
|
|
|
54
|
+
export default function App() {
|
|
55
|
+
return jsx(Outlet, {});
|
|
56
|
+
}
|
|
57
|
+
|
|
52
58
|
{% endblock %}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { createContext, useContext, useMemo, useReducer, useState, createElement } from "react"
|
|
2
|
-
import { applyDelta, Event, hydrateClientStorage, useEventLoop, refs } from "$/utils/state
|
|
1
|
+
import { createContext, useContext, useMemo, useReducer, useState, createElement, useEffect } from "react"
|
|
2
|
+
import { applyDelta, Event, hydrateClientStorage, useEventLoop, refs } from "$/utils/state"
|
|
3
|
+
import { jsx } from "@emotion/react";
|
|
3
4
|
|
|
4
5
|
{% if initial_state %}
|
|
5
6
|
export const initialState = {{ initial_state|json_dumps }}
|
|
@@ -77,7 +78,21 @@ export function UploadFilesProvider({ children }) {
|
|
|
77
78
|
delete newFilesById[id]
|
|
78
79
|
return newFilesById
|
|
79
80
|
})
|
|
80
|
-
return createElement(
|
|
81
|
+
return createElement(
|
|
82
|
+
UploadFilesContext.Provider,
|
|
83
|
+
{ value: [filesById, setFilesById] },
|
|
84
|
+
children
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function ClientSide(component) {
|
|
89
|
+
return ({ children, ...props }) => {
|
|
90
|
+
const [Component, setComponent] = useState(null);
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
setComponent(component);
|
|
93
|
+
}, []);
|
|
94
|
+
return Component ? jsx(Component, props, children) : null;
|
|
95
|
+
};
|
|
81
96
|
}
|
|
82
97
|
|
|
83
98
|
export function EventLoopProvider({ children }) {
|
|
@@ -87,7 +102,11 @@ export function EventLoopProvider({ children }) {
|
|
|
87
102
|
initialEvents,
|
|
88
103
|
clientStorage,
|
|
89
104
|
)
|
|
90
|
-
return createElement(
|
|
105
|
+
return createElement(
|
|
106
|
+
EventLoopContext.Provider,
|
|
107
|
+
{ value: [addEvents, connectErrors] },
|
|
108
|
+
children
|
|
109
|
+
);
|
|
91
110
|
}
|
|
92
111
|
|
|
93
112
|
export function StateProvider({ children }) {
|
|
@@ -106,7 +125,7 @@ export function StateProvider({ children }) {
|
|
|
106
125
|
{% for state_name in initial_state %}
|
|
107
126
|
createElement(StateContexts.{{state_name|var_name}},{value: {{state_name|var_name}}},
|
|
108
127
|
{% endfor %}
|
|
109
|
-
createElement(DispatchContext
|
|
110
|
-
{% for state_name in initial_state
|
|
128
|
+
createElement(DispatchContext, {value: dispatchers}, children)
|
|
129
|
+
{% for state_name in initial_state %}){% endfor %}
|
|
111
130
|
)
|
|
112
131
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { startTransition } from "react";
|
|
2
|
+
import { hydrateRoot } from "react-dom/client";
|
|
3
|
+
import { HydratedRouter } from "react-router/dom";
|
|
4
|
+
import { createElement } from "react";
|
|
5
|
+
|
|
6
|
+
startTransition(() => {
|
|
7
|
+
hydrateRoot(document, createElement(HydratedRouter));
|
|
8
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { route } from "@react-router/dev/routes";
|
|
2
|
+
import { flatRoutes } from "@react-router/fs-routes";
|
|
3
|
+
|
|
4
|
+
export default [
|
|
5
|
+
route("*", "routes/[404]_._index.js"),
|
|
6
|
+
route("404", "routes/[404]_._index.js", { id: "404" }),
|
|
7
|
+
...(await flatRoutes({
|
|
8
|
+
ignoredRouteFiles: ["routes/\\[404\\]_._index.js"],
|
|
9
|
+
})),
|
|
10
|
+
];
|
|
@@ -1,45 +1,19 @@
|
|
|
1
|
-
import { useTheme } from "
|
|
2
|
-
import {
|
|
1
|
+
import { useTheme } from "$/utils/react-theme";
|
|
2
|
+
import { createElement } from "react";
|
|
3
3
|
import {
|
|
4
4
|
ColorModeContext,
|
|
5
5
|
defaultColorMode,
|
|
6
6
|
isDevMode,
|
|
7
7
|
lastCompiledTimeStamp,
|
|
8
|
-
} from "$/utils/context
|
|
8
|
+
} from "$/utils/context";
|
|
9
9
|
|
|
10
10
|
export default function RadixThemesColorModeProvider({ children }) {
|
|
11
11
|
const { theme, resolvedTheme, setTheme } = useTheme();
|
|
12
|
-
const [rawColorMode, setRawColorMode] = useState(defaultColorMode);
|
|
13
|
-
const [resolvedColorMode, setResolvedColorMode] = useState(
|
|
14
|
-
defaultColorMode === "dark" ? "dark" : "light",
|
|
15
|
-
);
|
|
16
|
-
const firstUpdate = useRef(true);
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
if (firstUpdate.current) {
|
|
19
|
-
firstUpdate.current = false;
|
|
20
|
-
setRawColorMode(theme);
|
|
21
|
-
setResolvedColorMode(resolvedTheme);
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
if (isDevMode) {
|
|
27
|
-
const lastCompiledTimeInLocalStorage =
|
|
28
|
-
localStorage.getItem("last_compiled_time");
|
|
29
|
-
if (lastCompiledTimeInLocalStorage !== lastCompiledTimeStamp) {
|
|
30
|
-
// on app startup, make sure the application color mode is persisted correctly.
|
|
31
|
-
setTheme(defaultColorMode);
|
|
32
|
-
localStorage.setItem("last_compiled_time", lastCompiledTimeStamp);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
setRawColorMode(theme);
|
|
37
|
-
setResolvedColorMode(resolvedTheme);
|
|
38
|
-
}, [theme, resolvedTheme]);
|
|
39
12
|
|
|
40
13
|
const toggleColorMode = () => {
|
|
41
14
|
setTheme(resolvedTheme === "light" ? "dark" : "light");
|
|
42
15
|
};
|
|
16
|
+
|
|
43
17
|
const setColorMode = (mode) => {
|
|
44
18
|
const allowedModes = ["light", "dark", "system"];
|
|
45
19
|
if (!allowedModes.includes(mode)) {
|
|
@@ -50,10 +24,16 @@ export default function RadixThemesColorModeProvider({ children }) {
|
|
|
50
24
|
}
|
|
51
25
|
setTheme(mode);
|
|
52
26
|
};
|
|
27
|
+
|
|
53
28
|
return createElement(
|
|
54
|
-
ColorModeContext,
|
|
29
|
+
ColorModeContext.Provider,
|
|
55
30
|
{
|
|
56
|
-
value: {
|
|
31
|
+
value: {
|
|
32
|
+
rawColorMode: theme,
|
|
33
|
+
resolvedColorMode: resolvedTheme,
|
|
34
|
+
toggleColorMode,
|
|
35
|
+
setColorMode,
|
|
36
|
+
},
|
|
57
37
|
},
|
|
58
38
|
children,
|
|
59
39
|
);
|
|
@@ -1,41 +1,43 @@
|
|
|
1
1
|
import { useEffect, useRef, useState } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { useLocation, useNavigate } from "react-router-dom";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* React hook for use in
|
|
5
|
+
* React hook for use in NotFound page to enable client-side routing.
|
|
6
6
|
*
|
|
7
|
-
* Uses
|
|
8
|
-
* the
|
|
7
|
+
* Uses React Router to redirect to the provided URL when loading
|
|
8
|
+
* the NotFound page (for example as a fallback in static hosting situations).
|
|
9
9
|
*
|
|
10
10
|
* @returns {boolean} routeNotFound - true if the current route is an actual 404
|
|
11
11
|
*/
|
|
12
12
|
export const useClientSideRouting = () => {
|
|
13
13
|
const [routeNotFound, setRouteNotFound] = useState(false);
|
|
14
14
|
const didRedirect = useRef(false);
|
|
15
|
-
const
|
|
15
|
+
const location = useLocation();
|
|
16
|
+
const navigate = useNavigate();
|
|
17
|
+
|
|
16
18
|
useEffect(() => {
|
|
17
|
-
if (
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
didRedirect.current = true; // never redirect twice to avoid "Hard Navigate" error
|
|
19
|
+
if (!didRedirect.current) {
|
|
20
|
+
// have not tried redirecting yet
|
|
21
|
+
didRedirect.current = true; // never redirect twice to avoid navigation loops
|
|
22
|
+
|
|
22
23
|
// attempt to redirect to the route in the browser address bar once
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
const path = window.location.pathname;
|
|
25
|
+
const search = window.location.search;
|
|
26
|
+
|
|
27
|
+
// Use navigate instead of replace
|
|
28
|
+
navigate(path + search, { replace: true })
|
|
28
29
|
.then(() => {
|
|
29
|
-
// Check if
|
|
30
|
-
|
|
30
|
+
// Check if we're still on a NotFound route
|
|
31
|
+
// Note: This depends on how your routes are set up
|
|
32
|
+
if (location.pathname === path) {
|
|
31
33
|
setRouteNotFound(true); // Mark as an actual 404
|
|
32
34
|
}
|
|
33
35
|
})
|
|
34
|
-
.catch((
|
|
36
|
+
.catch(() => {
|
|
35
37
|
setRouteNotFound(true); // navigation failed, so this is a real 404
|
|
36
38
|
});
|
|
37
39
|
}
|
|
38
|
-
}, [
|
|
40
|
+
}, [location, navigate]);
|
|
39
41
|
|
|
40
42
|
// Return the reactive bool, to avoid flashing 404 page until we know for sure
|
|
41
43
|
// the route is not found.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
useState,
|
|
5
|
+
useEffect,
|
|
6
|
+
createElement,
|
|
7
|
+
useRef,
|
|
8
|
+
useMemo,
|
|
9
|
+
} from "react";
|
|
10
|
+
|
|
11
|
+
import { isDevMode, lastCompiledTimeStamp } from "$/utils/context";
|
|
12
|
+
|
|
13
|
+
const ThemeContext = createContext();
|
|
14
|
+
|
|
15
|
+
export function ThemeProvider({ children, defaultTheme = "system" }) {
|
|
16
|
+
const [theme, setTheme] = useState(defaultTheme);
|
|
17
|
+
const [systemTheme, setSystemTheme] = useState(
|
|
18
|
+
defaultTheme !== "system" ? defaultTheme : "light",
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const firstRender = useRef(true);
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (!firstRender.current) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
firstRender.current = false;
|
|
29
|
+
|
|
30
|
+
if (isDevMode) {
|
|
31
|
+
const lastCompiledTimeInLocalStorage =
|
|
32
|
+
localStorage.getItem("last_compiled_time");
|
|
33
|
+
if (lastCompiledTimeInLocalStorage !== lastCompiledTimeStamp) {
|
|
34
|
+
// on app startup, make sure the application color mode is persisted correctly.
|
|
35
|
+
localStorage.setItem("last_compiled_time", lastCompiledTimeStamp);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Load saved theme from localStorage
|
|
41
|
+
const savedTheme = localStorage.getItem("theme") || defaultTheme;
|
|
42
|
+
setTheme(savedTheme);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const resolvedTheme = useMemo(
|
|
46
|
+
() => (theme === "system" ? systemTheme : theme),
|
|
47
|
+
[theme, systemTheme],
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
// Set up media query for system preference detection
|
|
52
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
53
|
+
|
|
54
|
+
// Listen for system preference changes
|
|
55
|
+
const handleChange = () => {
|
|
56
|
+
setSystemTheme(mediaQuery.matches ? "dark" : "light");
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
handleChange();
|
|
60
|
+
|
|
61
|
+
mediaQuery.addEventListener("change", handleChange);
|
|
62
|
+
|
|
63
|
+
return () => {
|
|
64
|
+
mediaQuery.removeEventListener("change", handleChange);
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Save theme to localStorage whenever it changes
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
localStorage.setItem("theme", theme);
|
|
71
|
+
}, [theme]);
|
|
72
|
+
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
const root = window.document.documentElement;
|
|
75
|
+
root.classList.remove("light", "dark");
|
|
76
|
+
root.classList.add(resolvedTheme);
|
|
77
|
+
root.style.colorScheme = resolvedTheme;
|
|
78
|
+
}, [resolvedTheme]);
|
|
79
|
+
|
|
80
|
+
return createElement(
|
|
81
|
+
ThemeContext.Provider,
|
|
82
|
+
{ value: { theme, resolvedTheme, setTheme } },
|
|
83
|
+
children,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function useTheme() {
|
|
88
|
+
return useContext(ThemeContext);
|
|
89
|
+
}
|