@rangojs/router 0.0.0-experimental.2
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.
- package/CLAUDE.md +7 -0
- package/README.md +19 -0
- package/dist/vite/index.js +1298 -0
- package/package.json +140 -0
- package/skills/caching/SKILL.md +319 -0
- package/skills/document-cache/SKILL.md +152 -0
- package/skills/hooks/SKILL.md +359 -0
- package/skills/intercept/SKILL.md +292 -0
- package/skills/layout/SKILL.md +216 -0
- package/skills/loader/SKILL.md +365 -0
- package/skills/middleware/SKILL.md +442 -0
- package/skills/parallel/SKILL.md +255 -0
- package/skills/route/SKILL.md +141 -0
- package/skills/router-setup/SKILL.md +403 -0
- package/skills/theme/SKILL.md +54 -0
- package/skills/typesafety/SKILL.md +352 -0
- package/src/__mocks__/version.ts +6 -0
- package/src/__tests__/component-utils.test.ts +76 -0
- package/src/__tests__/route-definition.test.ts +63 -0
- package/src/__tests__/urls.test.tsx +436 -0
- package/src/browser/event-controller.ts +876 -0
- package/src/browser/index.ts +18 -0
- package/src/browser/link-interceptor.ts +121 -0
- package/src/browser/lru-cache.ts +69 -0
- package/src/browser/merge-segment-loaders.ts +126 -0
- package/src/browser/navigation-bridge.ts +893 -0
- package/src/browser/navigation-client.ts +162 -0
- package/src/browser/navigation-store.ts +823 -0
- package/src/browser/partial-update.ts +559 -0
- package/src/browser/react/Link.tsx +248 -0
- package/src/browser/react/NavigationProvider.tsx +275 -0
- package/src/browser/react/ScrollRestoration.tsx +94 -0
- package/src/browser/react/context.ts +53 -0
- package/src/browser/react/index.ts +52 -0
- package/src/browser/react/location-state-shared.ts +120 -0
- package/src/browser/react/location-state.ts +62 -0
- package/src/browser/react/use-action.ts +240 -0
- package/src/browser/react/use-client-cache.ts +56 -0
- package/src/browser/react/use-handle.ts +178 -0
- package/src/browser/react/use-href.tsx +208 -0
- package/src/browser/react/use-link-status.ts +134 -0
- package/src/browser/react/use-navigation.ts +150 -0
- package/src/browser/react/use-segments.ts +188 -0
- package/src/browser/request-controller.ts +164 -0
- package/src/browser/rsc-router.tsx +353 -0
- package/src/browser/scroll-restoration.ts +324 -0
- package/src/browser/server-action-bridge.ts +747 -0
- package/src/browser/shallow.ts +35 -0
- package/src/browser/types.ts +464 -0
- package/src/cache/__tests__/document-cache.test.ts +522 -0
- package/src/cache/__tests__/memory-segment-store.test.ts +487 -0
- package/src/cache/__tests__/memory-store.test.ts +484 -0
- package/src/cache/cache-scope.ts +565 -0
- package/src/cache/cf/__tests__/cf-cache-store.test.ts +428 -0
- package/src/cache/cf/cf-cache-store.ts +428 -0
- package/src/cache/cf/index.ts +19 -0
- package/src/cache/document-cache.ts +340 -0
- package/src/cache/index.ts +58 -0
- package/src/cache/memory-segment-store.ts +150 -0
- package/src/cache/memory-store.ts +253 -0
- package/src/cache/types.ts +387 -0
- package/src/client.rsc.tsx +88 -0
- package/src/client.tsx +621 -0
- package/src/component-utils.ts +76 -0
- package/src/components/DefaultDocument.tsx +23 -0
- package/src/default-error-boundary.tsx +88 -0
- package/src/deps/browser.ts +8 -0
- package/src/deps/html-stream-client.ts +2 -0
- package/src/deps/html-stream-server.ts +2 -0
- package/src/deps/rsc.ts +10 -0
- package/src/deps/ssr.ts +2 -0
- package/src/errors.ts +259 -0
- package/src/handle.ts +120 -0
- package/src/handles/MetaTags.tsx +193 -0
- package/src/handles/index.ts +6 -0
- package/src/handles/meta.ts +247 -0
- package/src/href-client.ts +128 -0
- package/src/href-context.ts +33 -0
- package/src/href.ts +177 -0
- package/src/index.rsc.ts +79 -0
- package/src/index.ts +87 -0
- package/src/loader.rsc.ts +204 -0
- package/src/loader.ts +47 -0
- package/src/network-error-thrower.tsx +21 -0
- package/src/outlet-context.ts +15 -0
- package/src/root-error-boundary.tsx +277 -0
- package/src/route-content-wrapper.tsx +198 -0
- package/src/route-definition.ts +1371 -0
- package/src/route-map-builder.ts +146 -0
- package/src/route-types.ts +198 -0
- package/src/route-utils.ts +89 -0
- package/src/router/__tests__/match-context.test.ts +104 -0
- package/src/router/__tests__/match-pipelines.test.ts +537 -0
- package/src/router/__tests__/match-result.test.ts +566 -0
- package/src/router/__tests__/on-error.test.ts +935 -0
- package/src/router/__tests__/pattern-matching.test.ts +577 -0
- package/src/router/error-handling.ts +287 -0
- package/src/router/handler-context.ts +158 -0
- package/src/router/loader-resolution.ts +326 -0
- package/src/router/manifest.ts +138 -0
- package/src/router/match-context.ts +264 -0
- package/src/router/match-middleware/background-revalidation.ts +236 -0
- package/src/router/match-middleware/cache-lookup.ts +261 -0
- package/src/router/match-middleware/cache-store.ts +266 -0
- package/src/router/match-middleware/index.ts +81 -0
- package/src/router/match-middleware/intercept-resolution.ts +268 -0
- package/src/router/match-middleware/segment-resolution.ts +174 -0
- package/src/router/match-pipelines.ts +214 -0
- package/src/router/match-result.ts +214 -0
- package/src/router/metrics.ts +62 -0
- package/src/router/middleware.test.ts +1355 -0
- package/src/router/middleware.ts +748 -0
- package/src/router/pattern-matching.ts +272 -0
- package/src/router/revalidation.ts +190 -0
- package/src/router/router-context.ts +299 -0
- package/src/router/types.ts +96 -0
- package/src/router.ts +3876 -0
- package/src/rsc/__tests__/helpers.test.ts +175 -0
- package/src/rsc/handler.ts +1060 -0
- package/src/rsc/helpers.ts +64 -0
- package/src/rsc/index.ts +56 -0
- package/src/rsc/nonce.ts +18 -0
- package/src/rsc/types.ts +237 -0
- package/src/segment-system.tsx +456 -0
- package/src/server/__tests__/request-context.test.ts +171 -0
- package/src/server/context.ts +417 -0
- package/src/server/handle-store.ts +230 -0
- package/src/server/loader-registry.ts +174 -0
- package/src/server/request-context.ts +554 -0
- package/src/server/root-layout.tsx +10 -0
- package/src/server/tsconfig.json +14 -0
- package/src/server.ts +146 -0
- package/src/ssr/__tests__/ssr-handler.test.tsx +188 -0
- package/src/ssr/index.tsx +234 -0
- package/src/theme/ThemeProvider.tsx +291 -0
- package/src/theme/ThemeScript.tsx +61 -0
- package/src/theme/__tests__/theme.test.ts +120 -0
- package/src/theme/constants.ts +55 -0
- package/src/theme/index.ts +58 -0
- package/src/theme/theme-context.ts +70 -0
- package/src/theme/theme-script.ts +152 -0
- package/src/theme/types.ts +182 -0
- package/src/theme/use-theme.ts +44 -0
- package/src/types.ts +1561 -0
- package/src/urls.ts +726 -0
- package/src/use-loader.tsx +346 -0
- package/src/vite/__tests__/expose-loader-id.test.ts +117 -0
- package/src/vite/expose-action-id.ts +344 -0
- package/src/vite/expose-handle-id.ts +209 -0
- package/src/vite/expose-loader-id.ts +357 -0
- package/src/vite/expose-location-state-id.ts +177 -0
- package/src/vite/index.ts +787 -0
- package/src/vite/package-resolution.ts +125 -0
- package/src/vite/version.d.ts +12 -0
- package/src/vite/virtual-entries.ts +109 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates an inline script for FOUC prevention.
|
|
3
|
+
*
|
|
4
|
+
* This script runs synchronously before page paint to:
|
|
5
|
+
* 1. Read theme from cookie or localStorage
|
|
6
|
+
* 2. Detect system preference if theme is "system"
|
|
7
|
+
* 3. Apply theme to HTML element via class or data attribute
|
|
8
|
+
* 4. Optionally set color-scheme CSS property
|
|
9
|
+
*
|
|
10
|
+
* The script is minified and inlined in <head> before any other content.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { ResolvedThemeConfig } from "./types.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Generate the inline script for theme initialization
|
|
17
|
+
*
|
|
18
|
+
* The script is designed to:
|
|
19
|
+
* - Run synchronously before paint (blocking)
|
|
20
|
+
* - Be as small as possible to minimize blocking time
|
|
21
|
+
* - Work without any external dependencies
|
|
22
|
+
* - Handle all edge cases (no localStorage, no cookie, etc.)
|
|
23
|
+
*/
|
|
24
|
+
export function generateThemeScript(config: ResolvedThemeConfig): string {
|
|
25
|
+
// Build the script as a string, then minify
|
|
26
|
+
const script = `
|
|
27
|
+
(function() {
|
|
28
|
+
var storageKey = ${JSON.stringify(config.storageKey)};
|
|
29
|
+
var defaultTheme = ${JSON.stringify(config.defaultTheme)};
|
|
30
|
+
var attribute = ${JSON.stringify(config.attribute)};
|
|
31
|
+
var enableSystem = ${config.enableSystem};
|
|
32
|
+
var enableColorScheme = ${config.enableColorScheme};
|
|
33
|
+
var valueMap = ${JSON.stringify(config.value)};
|
|
34
|
+
var themes = ${JSON.stringify(config.themes)};
|
|
35
|
+
|
|
36
|
+
// Read theme from cookie or localStorage
|
|
37
|
+
function getStoredTheme() {
|
|
38
|
+
// Try cookie first (for SSR consistency)
|
|
39
|
+
var cookies = document.cookie.split(';');
|
|
40
|
+
for (var i = 0; i < cookies.length; i++) {
|
|
41
|
+
var cookie = cookies[i].trim();
|
|
42
|
+
if (cookie.indexOf(storageKey + '=') === 0) {
|
|
43
|
+
return decodeURIComponent(cookie.substring(storageKey.length + 1));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Fall back to localStorage
|
|
47
|
+
try {
|
|
48
|
+
return localStorage.getItem(storageKey);
|
|
49
|
+
} catch (e) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Get system preference
|
|
55
|
+
function getSystemTheme() {
|
|
56
|
+
if (typeof window !== 'undefined' && window.matchMedia) {
|
|
57
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
58
|
+
}
|
|
59
|
+
return 'light';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Resolve "system" to actual theme
|
|
63
|
+
function resolveTheme(theme) {
|
|
64
|
+
if (theme === 'system' && enableSystem) {
|
|
65
|
+
return getSystemTheme();
|
|
66
|
+
}
|
|
67
|
+
return theme;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Apply theme to HTML element
|
|
71
|
+
function applyTheme(theme) {
|
|
72
|
+
var resolved = resolveTheme(theme);
|
|
73
|
+
var value = valueMap[resolved] || resolved;
|
|
74
|
+
var el = document.documentElement;
|
|
75
|
+
|
|
76
|
+
// Apply attribute
|
|
77
|
+
if (attribute === 'class') {
|
|
78
|
+
// Remove all theme classes, then add current
|
|
79
|
+
for (var i = 0; i < themes.length; i++) {
|
|
80
|
+
var v = valueMap[themes[i]] || themes[i];
|
|
81
|
+
el.classList.remove(v);
|
|
82
|
+
}
|
|
83
|
+
el.classList.add(value);
|
|
84
|
+
} else {
|
|
85
|
+
el.setAttribute(attribute, value);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Set color-scheme for native dark mode support
|
|
89
|
+
if (enableColorScheme) {
|
|
90
|
+
el.style.colorScheme = resolved;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Get stored theme or use default
|
|
95
|
+
var stored = getStoredTheme();
|
|
96
|
+
var theme = stored && (stored === 'system' || themes.indexOf(stored) !== -1)
|
|
97
|
+
? stored
|
|
98
|
+
: defaultTheme;
|
|
99
|
+
|
|
100
|
+
// Apply immediately
|
|
101
|
+
applyTheme(theme);
|
|
102
|
+
|
|
103
|
+
// Listen for system preference changes (for "system" theme)
|
|
104
|
+
if (enableSystem && typeof window !== 'undefined' && window.matchMedia) {
|
|
105
|
+
try {
|
|
106
|
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function() {
|
|
107
|
+
var current = getStoredTheme() || defaultTheme;
|
|
108
|
+
if (current === 'system') {
|
|
109
|
+
applyTheme('system');
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
} catch (e) {
|
|
113
|
+
// Older browsers may not support addEventListener on MediaQueryList
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
})();
|
|
117
|
+
`;
|
|
118
|
+
|
|
119
|
+
// Minify by removing comments, extra whitespace, and newlines
|
|
120
|
+
return minifyScript(script);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Basic script minification
|
|
125
|
+
* Removes comments, extra whitespace, and unnecessary newlines
|
|
126
|
+
*/
|
|
127
|
+
function minifyScript(script: string): string {
|
|
128
|
+
return script
|
|
129
|
+
// Remove single-line comments
|
|
130
|
+
.replace(/\/\/.*$/gm, "")
|
|
131
|
+
// Remove multi-line comments
|
|
132
|
+
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
133
|
+
// Remove leading/trailing whitespace from lines
|
|
134
|
+
.split("\n")
|
|
135
|
+
.map((line) => line.trim())
|
|
136
|
+
.filter((line) => line.length > 0)
|
|
137
|
+
.join("")
|
|
138
|
+
// Collapse multiple spaces to single space
|
|
139
|
+
.replace(/\s+/g, " ")
|
|
140
|
+
// Remove spaces around operators and punctuation
|
|
141
|
+
.replace(/\s*([{};,=!<>()[\]+\-*/&|?:])\s*/g, "$1")
|
|
142
|
+
// Add back necessary spaces (e.g., "var x")
|
|
143
|
+
.replace(/(var|function|return|if|for|try|catch|typeof|else)\(/g, "$1 (")
|
|
144
|
+
.replace(/\)([a-zA-Z])/g, ") $1");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Generate nonce attribute string if nonce is provided
|
|
149
|
+
*/
|
|
150
|
+
export function getNonceAttribute(nonce?: string): string {
|
|
151
|
+
return nonce ? ` nonce="${nonce}"` : "";
|
|
152
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme type definitions for rsc-router theme system.
|
|
3
|
+
*
|
|
4
|
+
* The theme system provides:
|
|
5
|
+
* - Opt-in via router config
|
|
6
|
+
* - FOUC prevention via inline script
|
|
7
|
+
* - Server-side theme access via ctx.theme/ctx.setTheme
|
|
8
|
+
* - Client-side theme access via useTheme hook
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Theme value - stored in cookies/localStorage
|
|
13
|
+
*/
|
|
14
|
+
export type Theme = "light" | "dark" | "system";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Resolved theme - actual visual appearance (system preference resolved)
|
|
18
|
+
*/
|
|
19
|
+
export type ResolvedTheme = "light" | "dark";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Attribute used to apply theme to HTML element
|
|
23
|
+
* - "class": Adds theme value as CSS class (e.g., <html class="dark">)
|
|
24
|
+
* - "data-*": Sets data attribute (e.g., <html data-theme="dark">)
|
|
25
|
+
*/
|
|
26
|
+
export type ThemeAttribute = "class" | `data-${string}`;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Theme configuration for router
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const router = createRSCRouter<Env>({
|
|
34
|
+
* theme: {
|
|
35
|
+
* defaultTheme: "system",
|
|
36
|
+
* themes: ["light", "dark"],
|
|
37
|
+
* attribute: "class",
|
|
38
|
+
* storageKey: "theme",
|
|
39
|
+
* }
|
|
40
|
+
* });
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export interface ThemeConfig {
|
|
44
|
+
/**
|
|
45
|
+
* Default theme when no preference is stored
|
|
46
|
+
* @default "system"
|
|
47
|
+
*/
|
|
48
|
+
defaultTheme?: Theme;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Available theme options
|
|
52
|
+
* @default ["light", "dark"]
|
|
53
|
+
*/
|
|
54
|
+
themes?: string[];
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Attribute to apply to HTML element
|
|
58
|
+
* @default "class"
|
|
59
|
+
*/
|
|
60
|
+
attribute?: ThemeAttribute;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Key for cookie and localStorage
|
|
64
|
+
* @default "theme"
|
|
65
|
+
*/
|
|
66
|
+
storageKey?: string;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Enable system preference detection
|
|
70
|
+
* When true, "system" theme will resolve based on prefers-color-scheme
|
|
71
|
+
* @default true
|
|
72
|
+
*/
|
|
73
|
+
enableSystem?: boolean;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Set CSS color-scheme property on HTML element
|
|
77
|
+
* This enables native dark mode for form controls, scrollbars, etc.
|
|
78
|
+
* @default true
|
|
79
|
+
*/
|
|
80
|
+
enableColorScheme?: boolean;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Custom mapping of theme names to attribute values
|
|
84
|
+
* Useful when theme names don't match desired attribute values
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* value: {
|
|
89
|
+
* light: "light-mode",
|
|
90
|
+
* dark: "dark-mode",
|
|
91
|
+
* }
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
value?: Record<string, string>;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Resolved theme configuration with defaults applied
|
|
99
|
+
*/
|
|
100
|
+
export interface ResolvedThemeConfig {
|
|
101
|
+
defaultTheme: Theme;
|
|
102
|
+
themes: string[];
|
|
103
|
+
attribute: ThemeAttribute;
|
|
104
|
+
storageKey: string;
|
|
105
|
+
enableSystem: boolean;
|
|
106
|
+
enableColorScheme: boolean;
|
|
107
|
+
value: Record<string, string>;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Return type from useTheme hook
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```tsx
|
|
115
|
+
* function ThemeToggle() {
|
|
116
|
+
* const { theme, setTheme, resolvedTheme, themes } = useTheme();
|
|
117
|
+
*
|
|
118
|
+
* return (
|
|
119
|
+
* <select value={theme} onChange={e => setTheme(e.target.value as Theme)}>
|
|
120
|
+
* {themes.map(t => <option key={t}>{t}</option>)}
|
|
121
|
+
* </select>
|
|
122
|
+
* );
|
|
123
|
+
* }
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
export interface UseThemeReturn {
|
|
127
|
+
/**
|
|
128
|
+
* Current theme setting ("light" | "dark" | "system")
|
|
129
|
+
*/
|
|
130
|
+
theme: Theme;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Set the theme
|
|
134
|
+
*/
|
|
135
|
+
setTheme: (theme: Theme) => void;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Resolved theme based on system preference
|
|
139
|
+
* When theme is "system", this reflects the actual appearance
|
|
140
|
+
*/
|
|
141
|
+
resolvedTheme: ResolvedTheme;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Current system preference ("light" | "dark")
|
|
145
|
+
*/
|
|
146
|
+
systemTheme: ResolvedTheme;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Available theme options (includes "system" if enableSystem is true)
|
|
150
|
+
*/
|
|
151
|
+
themes: string[];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Props for ThemeProvider component
|
|
156
|
+
*/
|
|
157
|
+
export interface ThemeProviderProps {
|
|
158
|
+
/**
|
|
159
|
+
* Theme configuration
|
|
160
|
+
*/
|
|
161
|
+
config: ResolvedThemeConfig;
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Initial theme from server (from cookie)
|
|
165
|
+
*/
|
|
166
|
+
initialTheme?: Theme;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Children to render
|
|
170
|
+
*/
|
|
171
|
+
children: React.ReactNode;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Context value for ThemeContext
|
|
176
|
+
*/
|
|
177
|
+
export interface ThemeContextValue extends UseThemeReturn {
|
|
178
|
+
/**
|
|
179
|
+
* Full theme configuration
|
|
180
|
+
*/
|
|
181
|
+
config: ResolvedThemeConfig;
|
|
182
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* useTheme - Hook for accessing theme state in client components.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* import { useTheme } from "@rangojs/router/theme";
|
|
9
|
+
*
|
|
10
|
+
* function ThemeToggle() {
|
|
11
|
+
* const { theme, setTheme, resolvedTheme, themes } = useTheme();
|
|
12
|
+
*
|
|
13
|
+
* return (
|
|
14
|
+
* <select value={theme} onChange={e => setTheme(e.target.value)}>
|
|
15
|
+
* {themes.map(t => <option key={t}>{t}</option>)}
|
|
16
|
+
* </select>
|
|
17
|
+
* );
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { requireThemeContext } from "./theme-context.js";
|
|
23
|
+
import type { UseThemeReturn } from "./types.js";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Hook to access theme state and methods.
|
|
27
|
+
*
|
|
28
|
+
* Must be used within a ThemeProvider (which is automatically included
|
|
29
|
+
* in NavigationProvider when theme is enabled in router config).
|
|
30
|
+
*
|
|
31
|
+
* @returns Theme state and methods
|
|
32
|
+
* @throws Error if used outside ThemeProvider
|
|
33
|
+
*/
|
|
34
|
+
export function useTheme(): UseThemeReturn {
|
|
35
|
+
const ctx = requireThemeContext();
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
theme: ctx.theme,
|
|
39
|
+
setTheme: ctx.setTheme,
|
|
40
|
+
resolvedTheme: ctx.resolvedTheme,
|
|
41
|
+
systemTheme: ctx.systemTheme,
|
|
42
|
+
themes: ctx.themes,
|
|
43
|
+
};
|
|
44
|
+
}
|