@riktajs/react 0.10.3 → 0.11.4
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/README.md +37 -4
- package/dist/index.cjs +65 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +65 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -172,13 +172,17 @@ function Breadcrumbs() {
|
|
|
172
172
|
|
|
173
173
|
### `useSsrData()`
|
|
174
174
|
|
|
175
|
-
Access SSR data passed from server via `window.__SSR_DATA__`.
|
|
175
|
+
Access SSR data passed from server via `window.__SSR_DATA__`. The data structure includes:
|
|
176
|
+
- `data`: The actual page data from the controller
|
|
177
|
+
- `url`: The current URL
|
|
178
|
+
- `title`: Page title (from `@Ssr` decorator)
|
|
179
|
+
- `description`: Page description (from `@Ssr` decorator)
|
|
176
180
|
|
|
177
181
|
```tsx
|
|
178
182
|
import { useSsrData } from '@riktajs/react';
|
|
179
183
|
|
|
180
184
|
interface PageData {
|
|
181
|
-
|
|
185
|
+
page: string;
|
|
182
186
|
items: Array<{ id: string; name: string }>;
|
|
183
187
|
}
|
|
184
188
|
|
|
@@ -187,11 +191,15 @@ function ItemList() {
|
|
|
187
191
|
|
|
188
192
|
if (!ssrData) return <div>Loading...</div>;
|
|
189
193
|
|
|
194
|
+
// Access page data
|
|
195
|
+
const { data, title, url } = ssrData;
|
|
196
|
+
|
|
190
197
|
return (
|
|
191
198
|
<div>
|
|
192
|
-
<h1>{
|
|
199
|
+
<h1>{title ?? data.page}</h1>
|
|
200
|
+
<p>Current URL: {url}</p>
|
|
193
201
|
<ul>
|
|
194
|
-
{
|
|
202
|
+
{data.items.map(item => (
|
|
195
203
|
<li key={item.id}>{item.name}</li>
|
|
196
204
|
))}
|
|
197
205
|
</ul>
|
|
@@ -200,6 +208,31 @@ function ItemList() {
|
|
|
200
208
|
}
|
|
201
209
|
```
|
|
202
210
|
|
|
211
|
+
### Client-Side Navigation with SSR Data Fetching
|
|
212
|
+
|
|
213
|
+
When navigating client-side using `<Link>` or `navigate()`, `RiktaProvider` automatically fetches the SSR data for the new page from the server. This ensures:
|
|
214
|
+
|
|
215
|
+
1. **No page flash**: Data is fetched before the route changes
|
|
216
|
+
2. **Consistent data structure**: Same data shape as initial SSR
|
|
217
|
+
3. **SEO metadata**: Title and description are updated automatically
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
// App.tsx - Route based on ssrData.url for data consistency
|
|
221
|
+
function App() {
|
|
222
|
+
const ssrData = useSsrData<{ page: string }>();
|
|
223
|
+
|
|
224
|
+
// Use ssrData.url for routing to ensure data and route are in sync
|
|
225
|
+
const pathname = ssrData?.url?.split('?')[0] ?? '/';
|
|
226
|
+
|
|
227
|
+
return (
|
|
228
|
+
<Layout title={ssrData?.title}>
|
|
229
|
+
{pathname === '/about' && <AboutPage />}
|
|
230
|
+
{pathname === '/' && <HomePage />}
|
|
231
|
+
</Layout>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
203
236
|
### `useHydration()`
|
|
204
237
|
|
|
205
238
|
Track hydration state for client-only rendering.
|
package/dist/index.cjs
CHANGED
|
@@ -13,14 +13,26 @@ var defaultRouterContext = {
|
|
|
13
13
|
},
|
|
14
14
|
params: {},
|
|
15
15
|
setParams: () => {
|
|
16
|
-
}
|
|
16
|
+
},
|
|
17
|
+
isNavigating: false
|
|
17
18
|
};
|
|
18
19
|
var RouterContext = react.createContext(defaultRouterContext);
|
|
19
20
|
RouterContext.displayName = "RiktaRouterContext";
|
|
20
21
|
var SsrContext = react.createContext(null);
|
|
21
22
|
SsrContext.displayName = "RiktaSsrContext";
|
|
22
|
-
function getLocationInfo() {
|
|
23
|
+
function getLocationInfo(ssrUrl) {
|
|
23
24
|
if (typeof window === "undefined") {
|
|
25
|
+
if (ssrUrl) {
|
|
26
|
+
try {
|
|
27
|
+
const url = new URL(ssrUrl, "http://localhost");
|
|
28
|
+
return {
|
|
29
|
+
pathname: url.pathname,
|
|
30
|
+
search: url.search.slice(1),
|
|
31
|
+
href: ssrUrl
|
|
32
|
+
};
|
|
33
|
+
} catch {
|
|
34
|
+
}
|
|
35
|
+
}
|
|
24
36
|
return { pathname: "/", search: "", href: "/" };
|
|
25
37
|
}
|
|
26
38
|
return {
|
|
@@ -32,19 +44,42 @@ function getLocationInfo() {
|
|
|
32
44
|
}
|
|
33
45
|
function getSsrData() {
|
|
34
46
|
if (typeof window === "undefined") return void 0;
|
|
35
|
-
|
|
47
|
+
const rawData = window.__SSR_DATA__;
|
|
48
|
+
if (!rawData) return void 0;
|
|
49
|
+
return rawData;
|
|
50
|
+
}
|
|
51
|
+
async function fetchSsrData(url) {
|
|
52
|
+
try {
|
|
53
|
+
const response = await fetch(url, {
|
|
54
|
+
headers: {
|
|
55
|
+
"X-Rikta-Data": "1",
|
|
56
|
+
"Accept": "application/json"
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
if (response.ok) {
|
|
60
|
+
const data = await response.json();
|
|
61
|
+
return data;
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.warn("[RiktaReact] Failed to fetch SSR data:", error);
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
36
67
|
}
|
|
37
68
|
var RiktaProvider = ({
|
|
38
69
|
ssrData: initialSsrData,
|
|
39
70
|
initialParams = {},
|
|
40
71
|
children
|
|
41
72
|
}) => {
|
|
42
|
-
const [ssrData] = react.useState(() => {
|
|
73
|
+
const [ssrData, setSsrData] = react.useState(() => {
|
|
43
74
|
return initialSsrData ?? getSsrData() ?? null;
|
|
44
75
|
});
|
|
45
|
-
const [
|
|
76
|
+
const [isNavigating, setIsNavigating] = react.useState(false);
|
|
77
|
+
const [location, setLocation] = react.useState(() => {
|
|
78
|
+
const resolvedSsrData = initialSsrData ?? getSsrData();
|
|
79
|
+
return getLocationInfo(resolvedSsrData?.url);
|
|
80
|
+
});
|
|
46
81
|
const [params, setParams] = react.useState(initialParams);
|
|
47
|
-
const navigate = react.useCallback((url, options = {}) => {
|
|
82
|
+
const navigate = react.useCallback(async (url, options = {}) => {
|
|
48
83
|
const { replace = false, scroll = true, state } = options;
|
|
49
84
|
if (typeof window === "undefined") return;
|
|
50
85
|
let targetUrl;
|
|
@@ -58,6 +93,11 @@ var RiktaProvider = ({
|
|
|
58
93
|
window.location.href = url;
|
|
59
94
|
return;
|
|
60
95
|
}
|
|
96
|
+
setIsNavigating(true);
|
|
97
|
+
const newSsrData = await fetchSsrData(targetUrl.href);
|
|
98
|
+
if (newSsrData) {
|
|
99
|
+
setSsrData(newSsrData);
|
|
100
|
+
}
|
|
61
101
|
if (replace) {
|
|
62
102
|
window.history.replaceState(state ?? null, "", targetUrl.href);
|
|
63
103
|
} else {
|
|
@@ -71,12 +111,19 @@ var RiktaProvider = ({
|
|
|
71
111
|
if (scroll) {
|
|
72
112
|
window.scrollTo(0, 0);
|
|
73
113
|
}
|
|
74
|
-
|
|
114
|
+
setIsNavigating(false);
|
|
75
115
|
}, []);
|
|
76
116
|
react.useEffect(() => {
|
|
77
117
|
if (typeof window === "undefined") return;
|
|
78
|
-
const handlePopState = () => {
|
|
79
|
-
|
|
118
|
+
const handlePopState = async () => {
|
|
119
|
+
const newLocation = getLocationInfo();
|
|
120
|
+
setIsNavigating(true);
|
|
121
|
+
const newSsrData = await fetchSsrData(newLocation.href);
|
|
122
|
+
if (newSsrData) {
|
|
123
|
+
setSsrData(newSsrData);
|
|
124
|
+
}
|
|
125
|
+
setLocation(newLocation);
|
|
126
|
+
setIsNavigating(false);
|
|
80
127
|
};
|
|
81
128
|
window.addEventListener("popstate", handlePopState);
|
|
82
129
|
return () => window.removeEventListener("popstate", handlePopState);
|
|
@@ -87,8 +134,9 @@ var RiktaProvider = ({
|
|
|
87
134
|
href: location.href,
|
|
88
135
|
navigate,
|
|
89
136
|
params,
|
|
90
|
-
setParams
|
|
91
|
-
|
|
137
|
+
setParams,
|
|
138
|
+
isNavigating
|
|
139
|
+
}), [location.pathname, location.search, location.href, navigate, params, isNavigating]);
|
|
92
140
|
return /* @__PURE__ */ jsxRuntime.jsx(SsrContext.Provider, { value: ssrData, children: /* @__PURE__ */ jsxRuntime.jsx(RouterContext.Provider, { value: routerValue, children }) });
|
|
93
141
|
};
|
|
94
142
|
RiktaProvider.displayName = "RiktaProvider";
|
|
@@ -196,8 +244,8 @@ function useFetch(url, options = {}) {
|
|
|
196
244
|
});
|
|
197
245
|
const mountedRef = react.useRef(true);
|
|
198
246
|
const fetchIdRef = react.useRef(0);
|
|
199
|
-
const
|
|
200
|
-
if (skip) return;
|
|
247
|
+
const doFetch = react.useCallback(async (ignoreSkip = false) => {
|
|
248
|
+
if (skip && !ignoreSkip) return;
|
|
201
249
|
const fetchId = ++fetchIdRef.current;
|
|
202
250
|
setState((prev) => ({ ...prev, loading: true, error: null }));
|
|
203
251
|
try {
|
|
@@ -221,14 +269,14 @@ function useFetch(url, options = {}) {
|
|
|
221
269
|
}, [url, skip, JSON.stringify(fetchOptions), transform]);
|
|
222
270
|
react.useEffect(() => {
|
|
223
271
|
mountedRef.current = true;
|
|
224
|
-
|
|
272
|
+
doFetch();
|
|
225
273
|
return () => {
|
|
226
274
|
mountedRef.current = false;
|
|
227
275
|
};
|
|
228
|
-
}, [
|
|
276
|
+
}, [doFetch, ...deps]);
|
|
229
277
|
const refetch = react.useCallback(async () => {
|
|
230
|
-
await
|
|
231
|
-
}, [
|
|
278
|
+
await doFetch(true);
|
|
279
|
+
}, [doFetch]);
|
|
232
280
|
return {
|
|
233
281
|
...state,
|
|
234
282
|
refetch
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context/RouterContext.ts","../src/context/SsrContext.ts","../src/components/RiktaProvider.tsx","../src/hooks/useNavigation.ts","../src/components/Link.tsx","../src/hooks/useParams.ts","../src/hooks/useSearchParams.ts","../src/hooks/useLocation.ts","../src/hooks/useSsrData.ts","../src/hooks/useHydration.ts","../src/hooks/useFetch.ts","../src/hooks/useAction.ts"],"names":["createContext","useState","useCallback","useEffect","useMemo","jsx","useContext","useRef","actionResult"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createContext } from 'react';\nimport type { RouterContextValue } from '../types.js';\n\n/**\n * Default router context value for when no provider is present\n */\nconst defaultRouterContext: RouterContextValue = {\n pathname: '/',\n search: '',\n href: '/',\n navigate: () => {\n console.warn('[RiktaReact] Router context not initialized. Wrap your app with <RiktaProvider>');\n },\n params: {},\n setParams: () => {},\n};\n\n/**\n * React context for router state\n * Provides navigation utilities and current location info\n */\nexport const RouterContext = createContext<RouterContextValue>(defaultRouterContext);\n\nRouterContext.displayName = 'RiktaRouterContext';\n","import { createContext } from 'react';\nimport type { SsrData } from '../types.js';\n\n/**\n * React context for SSR data\n * Holds the server-rendered data passed via window.__SSR_DATA__\n */\nexport const SsrContext = createContext<SsrData | null>(null);\n\nSsrContext.displayName = 'RiktaSsrContext';\n","import { useState, useCallback, useEffect, useMemo, type FC } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\nimport { SsrContext } from '../context/SsrContext.js';\nimport type { RiktaProviderProps, SsrData, NavigateOptions } from '../types.js';\n\n/**\n * Get current location from window or fallback for SSR\n */\nfunction getLocationInfo() {\n if (typeof window === 'undefined') {\n return { pathname: '/', search: '', href: '/' };\n }\n return {\n pathname: window.location.pathname,\n search: window.location.search.slice(1), // Remove leading ?\n href: window.location.href,\n };\n}\n\n/**\n * Get SSR data from window\n */\nfunction getSsrData(): SsrData | undefined {\n if (typeof window === 'undefined') return undefined;\n return window.__SSR_DATA__;\n}\n\n/**\n * RiktaProvider - Main provider component for Rikta React utilities\n * \n * Provides routing context, SSR data, and navigation utilities to the app.\n * \n * @example\n * ```tsx\n * // In entry-client.tsx\n * import { RiktaProvider } from '@riktajs/react';\n * \n * hydrateRoot(\n * document.getElementById('root')!,\n * <RiktaProvider>\n * <App />\n * </RiktaProvider>\n * );\n * ```\n */\nexport const RiktaProvider: FC<RiktaProviderProps> = ({\n ssrData: initialSsrData,\n initialParams = {},\n children,\n}) => {\n // Initialize SSR data from window or props\n const [ssrData] = useState<SsrData | null>(() => {\n return initialSsrData ?? getSsrData() ?? null;\n });\n\n // Initialize location state\n const [location, setLocation] = useState(getLocationInfo);\n \n // Route params state\n const [params, setParams] = useState<Record<string, string>>(initialParams);\n\n /**\n * Navigate to a new URL using History API\n */\n const navigate = useCallback((url: string, options: NavigateOptions = {}) => {\n const { replace = false, scroll = true, state } = options;\n\n if (typeof window === 'undefined') return;\n\n // Handle full URLs vs relative paths\n let targetUrl: URL;\n try {\n targetUrl = new URL(url, window.location.origin);\n } catch {\n console.error(`[RiktaReact] Invalid URL: ${url}`);\n return;\n }\n\n // Only handle same-origin navigation\n if (targetUrl.origin !== window.location.origin) {\n window.location.href = url;\n return;\n }\n\n // Update history\n if (replace) {\n window.history.replaceState(state ?? null, '', targetUrl.href);\n } else {\n window.history.pushState(state ?? null, '', targetUrl.href);\n }\n\n // Update location state\n setLocation({\n pathname: targetUrl.pathname,\n search: targetUrl.search.slice(1),\n href: targetUrl.href,\n });\n\n // Scroll to top if requested\n if (scroll) {\n window.scrollTo(0, 0);\n }\n\n // Dispatch popstate event for any other listeners\n window.dispatchEvent(new PopStateEvent('popstate', { state }));\n }, []);\n\n // Listen for popstate (browser back/forward)\n useEffect(() => {\n if (typeof window === 'undefined') return;\n\n const handlePopState = () => {\n setLocation(getLocationInfo());\n };\n\n window.addEventListener('popstate', handlePopState);\n return () => window.removeEventListener('popstate', handlePopState);\n }, []);\n\n // Memoize router context value\n const routerValue = useMemo(() => ({\n pathname: location.pathname,\n search: location.search,\n href: location.href,\n navigate,\n params,\n setParams,\n }), [location.pathname, location.search, location.href, navigate, params]);\n\n return (\n <SsrContext.Provider value={ssrData}>\n <RouterContext.Provider value={routerValue}>\n {children}\n </RouterContext.Provider>\n </SsrContext.Provider>\n );\n};\n\nRiktaProvider.displayName = 'RiktaProvider';\n","import { useContext, useCallback } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\nimport type { NavigateOptions } from '../types.js';\n\n/**\n * Hook for programmatic navigation\n * \n * @returns Object with navigate function and current location info\n * \n * @example\n * ```tsx\n * import { useNavigation } from '@riktajs/react';\n * \n * function MyComponent() {\n * const { navigate, pathname } = useNavigation();\n * \n * const handleSubmit = async () => {\n * await saveData();\n * navigate('/success');\n * };\n * \n * return (\n * <button onClick={handleSubmit}>\n * Submit (current path: {pathname})\n * </button>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * const { navigate } = useNavigation();\n * \n * // Replace history entry (for redirects)\n * navigate('/login', { replace: true });\n * \n * // Don't scroll to top\n * navigate('/next', { scroll: false });\n * \n * // Pass state\n * navigate('/edit', { state: { from: 'list' } });\n * ```\n */\nexport function useNavigation() {\n const context = useContext(RouterContext);\n\n const navigate = useCallback(\n (url: string, options?: NavigateOptions) => {\n context.navigate(url, options);\n },\n [context.navigate]\n );\n\n return {\n /** Navigate to a new URL */\n navigate,\n /** Current pathname */\n pathname: context.pathname,\n /** Current search string (without ?) */\n search: context.search,\n /** Full href */\n href: context.href,\n };\n}\n","import { useCallback, type FC, type MouseEvent } from 'react';\nimport { useNavigation } from '../hooks/useNavigation.js';\nimport type { LinkProps } from '../types.js';\n\n/**\n * Link component for client-side navigation\n * \n * Renders an anchor tag that uses the History API for navigation\n * instead of causing a full page reload.\n * \n * @example\n * ```tsx\n * import { Link } from '@riktajs/react';\n * \n * function Nav() {\n * return (\n * <nav>\n * <Link href=\"/\">Home</Link>\n * <Link href=\"/about\">About</Link>\n * <Link href=\"/items/123\">Item 123</Link>\n * </nav>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * <Link href=\"/dashboard\" replace scroll={false}>\n * Dashboard\n * </Link>\n * ```\n */\nexport const Link: FC<LinkProps> = ({\n href,\n replace = false,\n scroll = true,\n prefetch = false,\n state,\n children,\n onClick,\n ...restProps\n}) => {\n const { navigate } = useNavigation();\n\n const handleClick = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n // Call user's onClick handler if provided\n onClick?.(e);\n\n // Don't handle if default was prevented\n if (e.defaultPrevented) return;\n\n // Don't handle modified clicks (open in new tab, etc.)\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n\n // Don't handle right clicks\n if (e.button !== 0) return;\n\n // Don't handle target=\"_blank\" etc.\n const target = (e.currentTarget as HTMLAnchorElement).target;\n if (target && target !== '_self') return;\n\n // Don't handle external links\n try {\n const url = new URL(href, window.location.origin);\n if (url.origin !== window.location.origin) return;\n } catch {\n return;\n }\n\n // Handle the navigation\n e.preventDefault();\n navigate(href, { replace, scroll, state });\n },\n [href, replace, scroll, state, navigate, onClick]\n );\n\n // Prefetch support could be added here in the future\n // using <link rel=\"prefetch\"> or Intersection Observer\n\n return (\n <a href={href} onClick={handleClick} {...restProps}>\n {children}\n </a>\n );\n};\n\nLink.displayName = 'Link';\n","import { useContext } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Hook to access route parameters\n * \n * Route parameters are extracted from the URL path by the server\n * and passed via SSR data. They're stored in the RouterContext.\n * \n * @returns Object with route parameter values\n * \n * @example\n * ```tsx\n * // For route /item/:id\n * import { useParams } from '@riktajs/react';\n * \n * function ItemPage() {\n * const { id } = useParams<{ id: string }>();\n * \n * return <h1>Item {id}</h1>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Multiple params - /users/:userId/posts/:postId\n * function PostPage() {\n * const { userId, postId } = useParams<{ userId: string; postId: string }>();\n * \n * return <h1>Post {postId} by User {userId}</h1>;\n * }\n * ```\n */\nexport function useParams<T extends Record<string, string> = Record<string, string>>(): T {\n const context = useContext(RouterContext);\n return context.params as T;\n}\n","import { useContext, useMemo } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Hook to access and manipulate URL search parameters\n * \n * @returns Tuple of [URLSearchParams, setSearchParams function]\n * \n * @example\n * ```tsx\n * import { useSearchParams } from '@riktajs/react';\n * \n * function SearchPage() {\n * const [searchParams, setSearchParams] = useSearchParams();\n * const query = searchParams.get('q') ?? '';\n * const page = parseInt(searchParams.get('page') ?? '1', 10);\n * \n * const handleSearch = (newQuery: string) => {\n * setSearchParams({ q: newQuery, page: '1' });\n * };\n * \n * const handleNextPage = () => {\n * setSearchParams({ q: query, page: String(page + 1) });\n * };\n * \n * return (\n * <div>\n * <input \n * value={query} \n * onChange={(e) => handleSearch(e.target.value)} \n * />\n * <button onClick={handleNextPage}>Next Page</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useSearchParams(): [URLSearchParams, (params: Record<string, string> | URLSearchParams) => void] {\n const context = useContext(RouterContext);\n\n const searchParams = useMemo(() => {\n return new URLSearchParams(context.search);\n }, [context.search]);\n\n const setSearchParams = useMemo(() => {\n return (params: Record<string, string> | URLSearchParams) => {\n const newParams = params instanceof URLSearchParams \n ? params \n : new URLSearchParams(params);\n \n const search = newParams.toString();\n const newUrl = search \n ? `${context.pathname}?${search}` \n : context.pathname;\n \n context.navigate(newUrl, { scroll: false });\n };\n }, [context.pathname, context.navigate]);\n\n return [searchParams, setSearchParams];\n}\n","import { useContext } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Location object returned by useLocation\n */\nexport interface Location {\n /** Current pathname (e.g., /items/123) */\n pathname: string;\n /** Current search string without ? (e.g., page=2&sort=asc) */\n search: string;\n /** Full href */\n href: string;\n /** Parsed search params */\n searchParams: URLSearchParams;\n}\n\n/**\n * Hook to access current location information\n * \n * @returns Location object with pathname, search, href, and searchParams\n * \n * @example\n * ```tsx\n * import { useLocation } from '@riktajs/react';\n * \n * function Breadcrumbs() {\n * const location = useLocation();\n * \n * return (\n * <nav>\n * Current path: {location.pathname}\n * {location.search && <span>?{location.search}</span>}\n * </nav>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Access search params\n * function FilterDisplay() {\n * const { searchParams } = useLocation();\n * const filter = searchParams.get('filter');\n * \n * return filter ? <span>Filtered by: {filter}</span> : null;\n * }\n * ```\n */\nexport function useLocation(): Location {\n const context = useContext(RouterContext);\n\n return {\n pathname: context.pathname,\n search: context.search,\n href: context.href,\n searchParams: new URLSearchParams(context.search),\n };\n}\n","import { useContext } from 'react';\nimport { SsrContext } from '../context/SsrContext.js';\nimport type { SsrData } from '../types.js';\n\n/**\n * Hook to access SSR data passed from server\n * \n * SSR data is passed via window.__SSR_DATA__ and contains\n * the initial data rendered on the server.\n * \n * @returns SSR data object or null if not available\n * \n * @example\n * ```tsx\n * import { useSsrData } from '@riktajs/react';\n * \n * interface PageData {\n * title: string;\n * items: Array<{ id: string; name: string }>;\n * }\n * \n * function ItemList() {\n * const ssrData = useSsrData<PageData>();\n * \n * if (!ssrData) {\n * return <div>Loading...</div>;\n * }\n * \n * return (\n * <div>\n * <h1>{ssrData.data.title}</h1>\n * <ul>\n * {ssrData.data.items.map(item => (\n * <li key={item.id}>{item.name}</li>\n * ))}\n * </ul>\n * </div>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Access just the data\n * function MyComponent() {\n * const ssrData = useSsrData<{ user: User }>();\n * const user = ssrData?.data.user;\n * \n * return user ? <UserProfile user={user} /> : <LoginPrompt />;\n * }\n * ```\n */\nexport function useSsrData<T = unknown>(): SsrData<T> | null {\n const context = useContext(SsrContext);\n return context as SsrData<T> | null;\n}\n","import { useState, useEffect } from 'react';\nimport type { HydrationState } from '../types.js';\n\n/**\n * Hook to track hydration state\n * \n * Useful for rendering different content during SSR vs after hydration,\n * or for avoiding hydration mismatches.\n * \n * @returns Hydration state object\n * \n * @example\n * ```tsx\n * import { useHydration } from '@riktajs/react';\n * \n * function TimeDisplay() {\n * const { isHydrated, isServer } = useHydration();\n * \n * // On server and initial render, show static content\n * // After hydration, show dynamic content\n * if (!isHydrated) {\n * return <span>Loading time...</span>;\n * }\n * \n * return <span>{new Date().toLocaleTimeString()}</span>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Avoid hydration mismatch with client-only content\n * function ClientOnlyComponent() {\n * const { isHydrated } = useHydration();\n * \n * if (!isHydrated) {\n * return null; // Or a placeholder\n * }\n * \n * return <SomeClientOnlyLibrary />;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Conditional rendering based on environment\n * function DebugPanel() {\n * const { isServer } = useHydration();\n * \n * // Never render on server, only after client hydration\n * if (isServer) return null;\n * \n * return <DevTools />;\n * }\n * ```\n */\nexport function useHydration(): HydrationState {\n const isServer = typeof window === 'undefined';\n const [isHydrated, setIsHydrated] = useState(false);\n\n useEffect(() => {\n setIsHydrated(true);\n }, []);\n\n return {\n isHydrated,\n isServer,\n };\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport type { FetchState } from '../types.js';\n\n/**\n * Options for useFetch hook\n */\nexport interface UseFetchOptions extends Omit<RequestInit, 'body'> {\n /** Skip initial fetch (useful for conditional fetching) */\n skip?: boolean;\n /** Dependencies that trigger refetch when changed */\n deps?: unknown[];\n /** Transform response before setting data */\n transform?: (data: unknown) => unknown;\n}\n\n/**\n * Hook for data fetching with loading and error states\n * \n * @param url URL to fetch from\n * @param options Fetch options\n * @returns Fetch state with data, loading, error, and refetch function\n * \n * @example\n * ```tsx\n * import { useFetch } from '@riktajs/react';\n * \n * interface User {\n * id: string;\n * name: string;\n * }\n * \n * function UserProfile({ userId }: { userId: string }) {\n * const { data, loading, error, refetch } = useFetch<User>(\n * `/api/users/${userId}`\n * );\n * \n * if (loading) return <Spinner />;\n * if (error) return <Error message={error} />;\n * if (!data) return null;\n * \n * return (\n * <div>\n * <h1>{data.name}</h1>\n * <button onClick={refetch}>Refresh</button>\n * </div>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * const { data } = useFetch<Item[]>('/api/items', {\n * headers: { 'Authorization': `Bearer ${token}` },\n * deps: [token], // Refetch when token changes\n * skip: !token, // Don't fetch until we have a token\n * });\n * ```\n * \n * @example\n * ```tsx\n * // With transform\n * const { data } = useFetch<{ results: Item[] }>('/api/search', {\n * transform: (res) => res.results, // Extract just the results array\n * });\n * ```\n */\nexport function useFetch<T = unknown>(\n url: string,\n options: UseFetchOptions = {}\n): FetchState<T> {\n const { skip = false, deps = [], transform, ...fetchOptions } = options;\n \n const [state, setState] = useState<Omit<FetchState<T>, 'refetch'>>({\n data: null,\n loading: !skip,\n error: null,\n });\n\n // Use ref to track if component is mounted\n const mountedRef = useRef(true);\n // Use ref to track current fetch to handle race conditions\n const fetchIdRef = useRef(0);\n\n const fetchData = useCallback(async () => {\n if (skip) return;\n\n const fetchId = ++fetchIdRef.current;\n setState(prev => ({ ...prev, loading: true, error: null }));\n\n try {\n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n let data = await response.json();\n\n // Apply transform if provided\n if (transform) {\n data = transform(data);\n }\n\n // Only update state if this is still the current fetch and component is mounted\n if (fetchId === fetchIdRef.current && mountedRef.current) {\n setState({ data: data as T, loading: false, error: null });\n }\n } catch (err) {\n if (fetchId === fetchIdRef.current && mountedRef.current) {\n const message = err instanceof Error ? err.message : 'An error occurred';\n setState({ data: null, loading: false, error: message });\n }\n }\n }, [url, skip, JSON.stringify(fetchOptions), transform]);\n\n // Fetch on mount and when dependencies change\n useEffect(() => {\n mountedRef.current = true;\n fetchData();\n\n return () => {\n mountedRef.current = false;\n };\n }, [fetchData, ...deps]);\n\n const refetch = useCallback(async () => {\n await fetchData();\n }, [fetchData]);\n\n return {\n ...state,\n refetch,\n };\n}\n","import { useState, useCallback } from 'react';\nimport type { ActionResult, ActionState } from '../types.js';\n\n/**\n * Options for useAction hook\n */\nexport interface UseActionOptions<TResult = unknown> {\n /** Callback on successful action */\n onSuccess?: (result: TResult) => void;\n /** Callback on action error */\n onError?: (error: string) => void;\n /** HTTP method to use */\n method?: 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n /** Additional headers */\n headers?: Record<string, string>;\n}\n\n/**\n * Hook for executing server actions (form submissions, mutations)\n * \n * @param url URL to send the action to\n * @param options Action options\n * @returns Action state with execute, pending, result, and reset\n * \n * @example\n * ```tsx\n * import { useAction } from '@riktajs/react';\n * \n * interface CreateItemInput {\n * name: string;\n * price: number;\n * }\n * \n * interface Item {\n * id: string;\n * name: string;\n * price: number;\n * }\n * \n * function CreateItemForm() {\n * const { execute, pending, result } = useAction<CreateItemInput, Item>(\n * '/api/items',\n * {\n * onSuccess: (item) => {\n * console.log('Created item:', item);\n * },\n * }\n * );\n * \n * const handleSubmit = async (e: FormEvent) => {\n * e.preventDefault();\n * const formData = new FormData(e.target as HTMLFormElement);\n * await execute({\n * name: formData.get('name') as string,\n * price: Number(formData.get('price')),\n * });\n * };\n * \n * return (\n * <form onSubmit={handleSubmit}>\n * <input name=\"name\" required />\n * <input name=\"price\" type=\"number\" required />\n * <button disabled={pending}>\n * {pending ? 'Creating...' : 'Create Item'}\n * </button>\n * {result?.error && <p className=\"error\">{result.error}</p>}\n * {result?.fieldErrors?.name && (\n * <p className=\"error\">{result.fieldErrors.name[0]}</p>\n * )}\n * </form>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // DELETE action\n * const { execute, pending } = useAction<{ id: string }, void>(\n * '/api/items',\n * { method: 'DELETE' }\n * );\n * \n * const handleDelete = () => execute({ id: itemId });\n * ```\n */\nexport function useAction<TInput = unknown, TResult = unknown>(\n url: string,\n options: UseActionOptions<TResult> = {}\n): ActionState<TInput, TResult> {\n const {\n onSuccess,\n onError,\n method = 'POST',\n headers: customHeaders = {},\n } = options;\n\n const [pending, setPending] = useState(false);\n const [result, setResult] = useState<ActionResult<TResult> | null>(null);\n\n const execute = useCallback(\n async (input: TInput): Promise<ActionResult<TResult>> => {\n setPending(true);\n setResult(null);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...customHeaders,\n },\n body: JSON.stringify(input),\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n // Handle error response\n const actionResult: ActionResult<TResult> = {\n success: false,\n error: data.message || data.error || `HTTP ${response.status}`,\n fieldErrors: data.fieldErrors,\n };\n setResult(actionResult);\n onError?.(actionResult.error!);\n return actionResult;\n }\n\n // Handle success response\n const actionResult: ActionResult<TResult> = {\n success: true,\n data: data as TResult,\n };\n setResult(actionResult);\n onSuccess?.(data as TResult);\n return actionResult;\n } catch (err) {\n const message = err instanceof Error ? err.message : 'An error occurred';\n const actionResult: ActionResult<TResult> = {\n success: false,\n error: message,\n };\n setResult(actionResult);\n onError?.(message);\n return actionResult;\n } finally {\n setPending(false);\n }\n },\n [url, method, JSON.stringify(customHeaders), onSuccess, onError]\n );\n\n const reset = useCallback(() => {\n setResult(null);\n }, []);\n\n return {\n execute,\n pending,\n result,\n reset,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/context/RouterContext.ts","../src/context/SsrContext.ts","../src/components/RiktaProvider.tsx","../src/hooks/useNavigation.ts","../src/components/Link.tsx","../src/hooks/useParams.ts","../src/hooks/useSearchParams.ts","../src/hooks/useLocation.ts","../src/hooks/useSsrData.ts","../src/hooks/useHydration.ts","../src/hooks/useFetch.ts","../src/hooks/useAction.ts"],"names":["createContext","useState","useCallback","useEffect","useMemo","jsx","useContext","useRef","actionResult"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createContext } from 'react';\nimport type { RouterContextValue } from '../types.js';\n\n/**\n * Default router context value for when no provider is present\n */\nconst defaultRouterContext: RouterContextValue = {\n pathname: '/',\n search: '',\n href: '/',\n navigate: () => {\n console.warn('[RiktaReact] Router context not initialized. Wrap your app with <RiktaProvider>');\n },\n params: {},\n setParams: () => {},\n isNavigating: false,\n};\n\n/**\n * React context for router state\n * Provides navigation utilities and current location info\n */\nexport const RouterContext = createContext<RouterContextValue>(defaultRouterContext);\n\nRouterContext.displayName = 'RiktaRouterContext';\n","import { createContext } from 'react';\nimport type { SsrData } from '../types.js';\n\n/**\n * React context for SSR data\n * Holds the server-rendered data passed via window.__SSR_DATA__\n */\nexport const SsrContext = createContext<SsrData | null>(null);\n\nSsrContext.displayName = 'RiktaSsrContext';\n","import { useState, useCallback, useEffect, useMemo, type FC } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\nimport { SsrContext } from '../context/SsrContext.js';\nimport type { RiktaProviderProps, SsrData, NavigateOptions } from '../types.js';\n\n/**\n * Get current location from window or fallback for SSR\n */\nfunction getLocationInfo(ssrUrl?: string) {\n if (typeof window === 'undefined') {\n // During SSR, use the URL from ssrData if available\n if (ssrUrl) {\n try {\n const url = new URL(ssrUrl, 'http://localhost');\n return {\n pathname: url.pathname,\n search: url.search.slice(1),\n href: ssrUrl,\n };\n } catch {\n // Fall through to default\n }\n }\n return { pathname: '/', search: '', href: '/' };\n }\n return {\n pathname: window.location.pathname,\n search: window.location.search.slice(1), // Remove leading ?\n href: window.location.href,\n };\n}\n\n/**\n * Get SSR data from window\n * Server now puts data in normalized format: { data, url, title, description }\n */\nfunction getSsrData(): SsrData | undefined {\n if (typeof window === 'undefined') return undefined;\n \n const rawData = window.__SSR_DATA__ as Record<string, unknown> | undefined;\n if (!rawData) return undefined;\n \n // Data should already be in normalized format from entry-server\n return rawData as unknown as SsrData;\n}\n\n/**\n * Fetch SSR data from server for client-side navigation\n */\nasync function fetchSsrData(url: string): Promise<SsrData | null> {\n try {\n const response = await fetch(url, {\n headers: {\n 'X-Rikta-Data': '1',\n 'Accept': 'application/json',\n },\n });\n \n if (response.ok) {\n const data = await response.json();\n return data as SsrData;\n }\n } catch (error) {\n console.warn('[RiktaReact] Failed to fetch SSR data:', error);\n }\n return null;\n}\n\n/**\n * RiktaProvider - Main provider component for Rikta React utilities\n * \n * Provides routing context, SSR data, and navigation utilities to the app.\n * Automatically fetches new page data during client-side navigation.\n * \n * @example\n * ```tsx\n * // In entry-client.tsx\n * import { RiktaProvider } from '@riktajs/react';\n * \n * hydrateRoot(\n * document.getElementById('root')!,\n * <RiktaProvider>\n * <App />\n * </RiktaProvider>\n * );\n * ```\n */\nexport const RiktaProvider: FC<RiktaProviderProps> = ({\n ssrData: initialSsrData,\n initialParams = {},\n children,\n}) => {\n // Initialize SSR data from window or props\n const [ssrData, setSsrData] = useState<SsrData | null>(() => {\n return initialSsrData ?? getSsrData() ?? null;\n });\n\n // Loading state for navigation\n const [isNavigating, setIsNavigating] = useState(false);\n\n // Initialize location state - use URL from ssrData for SSR consistency\n const [location, setLocation] = useState(() => {\n const resolvedSsrData = initialSsrData ?? getSsrData();\n return getLocationInfo(resolvedSsrData?.url);\n });\n \n // Route params state\n const [params, setParams] = useState<Record<string, string>>(initialParams);\n\n /**\n * Navigate to a new URL using History API with data fetching\n */\n const navigate = useCallback(async (url: string, options: NavigateOptions = {}) => {\n const { replace = false, scroll = true, state } = options;\n\n if (typeof window === 'undefined') return;\n\n // Handle full URLs vs relative paths\n let targetUrl: URL;\n try {\n targetUrl = new URL(url, window.location.origin);\n } catch {\n console.error(`[RiktaReact] Invalid URL: ${url}`);\n return;\n }\n\n // Only handle same-origin navigation\n if (targetUrl.origin !== window.location.origin) {\n window.location.href = url;\n return;\n }\n\n // Start navigation\n setIsNavigating(true);\n\n // Fetch new SSR data from server BEFORE updating anything else\n const newSsrData = await fetchSsrData(targetUrl.href);\n\n // Update SSR data FIRST (before location changes)\n if (newSsrData) {\n setSsrData(newSsrData);\n }\n\n // Update history\n if (replace) {\n window.history.replaceState(state ?? null, '', targetUrl.href);\n } else {\n window.history.pushState(state ?? null, '', targetUrl.href);\n }\n\n // Update location state AFTER data is ready\n setLocation({\n pathname: targetUrl.pathname,\n search: targetUrl.search.slice(1),\n href: targetUrl.href,\n });\n\n // Scroll to top if requested\n if (scroll) {\n window.scrollTo(0, 0);\n }\n\n // End navigation\n setIsNavigating(false);\n }, []);\n\n // Listen for popstate (browser back/forward) and fetch data\n useEffect(() => {\n if (typeof window === 'undefined') return;\n\n const handlePopState = async () => {\n const newLocation = getLocationInfo();\n \n // Fetch data for the new URL FIRST\n setIsNavigating(true);\n const newSsrData = await fetchSsrData(newLocation.href);\n \n // Update SSR data BEFORE location\n if (newSsrData) {\n setSsrData(newSsrData);\n }\n \n // Then update location\n setLocation(newLocation);\n setIsNavigating(false);\n };\n\n window.addEventListener('popstate', handlePopState);\n return () => window.removeEventListener('popstate', handlePopState);\n }, []);\n\n // Memoize router context value\n const routerValue = useMemo(() => ({\n pathname: location.pathname,\n search: location.search,\n href: location.href,\n navigate,\n params,\n setParams,\n isNavigating,\n }), [location.pathname, location.search, location.href, navigate, params, isNavigating]);\n\n return (\n <SsrContext.Provider value={ssrData}>\n <RouterContext.Provider value={routerValue}>\n {children}\n </RouterContext.Provider>\n </SsrContext.Provider>\n );\n};\n\nRiktaProvider.displayName = 'RiktaProvider';\n","import { useContext, useCallback } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\nimport type { NavigateOptions } from '../types.js';\n\n/**\n * Hook for programmatic navigation\n * \n * @returns Object with navigate function and current location info\n * \n * @example\n * ```tsx\n * import { useNavigation } from '@riktajs/react';\n * \n * function MyComponent() {\n * const { navigate, pathname } = useNavigation();\n * \n * const handleSubmit = async () => {\n * await saveData();\n * navigate('/success');\n * };\n * \n * return (\n * <button onClick={handleSubmit}>\n * Submit (current path: {pathname})\n * </button>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * const { navigate } = useNavigation();\n * \n * // Replace history entry (for redirects)\n * navigate('/login', { replace: true });\n * \n * // Don't scroll to top\n * navigate('/next', { scroll: false });\n * \n * // Pass state\n * navigate('/edit', { state: { from: 'list' } });\n * ```\n */\nexport function useNavigation() {\n const context = useContext(RouterContext);\n\n const navigate = useCallback(\n (url: string, options?: NavigateOptions) => {\n context.navigate(url, options);\n },\n [context.navigate]\n );\n\n return {\n /** Navigate to a new URL */\n navigate,\n /** Current pathname */\n pathname: context.pathname,\n /** Current search string (without ?) */\n search: context.search,\n /** Full href */\n href: context.href,\n };\n}\n","import { useCallback, type FC, type MouseEvent } from 'react';\nimport { useNavigation } from '../hooks/useNavigation.js';\nimport type { LinkProps } from '../types.js';\n\n/**\n * Link component for client-side navigation\n * \n * Renders an anchor tag that uses the History API for navigation\n * instead of causing a full page reload.\n * \n * @example\n * ```tsx\n * import { Link } from '@riktajs/react';\n * \n * function Nav() {\n * return (\n * <nav>\n * <Link href=\"/\">Home</Link>\n * <Link href=\"/about\">About</Link>\n * <Link href=\"/items/123\">Item 123</Link>\n * </nav>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * <Link href=\"/dashboard\" replace scroll={false}>\n * Dashboard\n * </Link>\n * ```\n */\nexport const Link: FC<LinkProps> = ({\n href,\n replace = false,\n scroll = true,\n prefetch = false,\n state,\n children,\n onClick,\n ...restProps\n}) => {\n const { navigate } = useNavigation();\n\n const handleClick = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n // Call user's onClick handler if provided\n onClick?.(e);\n\n // Don't handle if default was prevented\n if (e.defaultPrevented) return;\n\n // Don't handle modified clicks (open in new tab, etc.)\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n\n // Don't handle right clicks\n if (e.button !== 0) return;\n\n // Don't handle target=\"_blank\" etc.\n const target = (e.currentTarget as HTMLAnchorElement).target;\n if (target && target !== '_self') return;\n\n // Don't handle external links\n try {\n const url = new URL(href, window.location.origin);\n if (url.origin !== window.location.origin) return;\n } catch {\n return;\n }\n\n // Handle the navigation\n e.preventDefault();\n navigate(href, { replace, scroll, state });\n },\n [href, replace, scroll, state, navigate, onClick]\n );\n\n // Prefetch support could be added here in the future\n // using <link rel=\"prefetch\"> or Intersection Observer\n\n return (\n <a href={href} onClick={handleClick} {...restProps}>\n {children}\n </a>\n );\n};\n\nLink.displayName = 'Link';\n","import { useContext } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Hook to access route parameters\n * \n * Route parameters are extracted from the URL path by the server\n * and passed via SSR data. They're stored in the RouterContext.\n * \n * @returns Object with route parameter values\n * \n * @example\n * ```tsx\n * // For route /item/:id\n * import { useParams } from '@riktajs/react';\n * \n * function ItemPage() {\n * const { id } = useParams<{ id: string }>();\n * \n * return <h1>Item {id}</h1>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Multiple params - /users/:userId/posts/:postId\n * function PostPage() {\n * const { userId, postId } = useParams<{ userId: string; postId: string }>();\n * \n * return <h1>Post {postId} by User {userId}</h1>;\n * }\n * ```\n */\nexport function useParams<T extends Record<string, string> = Record<string, string>>(): T {\n const context = useContext(RouterContext);\n return context.params as T;\n}\n","import { useContext, useMemo } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Hook to access and manipulate URL search parameters\n * \n * @returns Tuple of [URLSearchParams, setSearchParams function]\n * \n * @example\n * ```tsx\n * import { useSearchParams } from '@riktajs/react';\n * \n * function SearchPage() {\n * const [searchParams, setSearchParams] = useSearchParams();\n * const query = searchParams.get('q') ?? '';\n * const page = parseInt(searchParams.get('page') ?? '1', 10);\n * \n * const handleSearch = (newQuery: string) => {\n * setSearchParams({ q: newQuery, page: '1' });\n * };\n * \n * const handleNextPage = () => {\n * setSearchParams({ q: query, page: String(page + 1) });\n * };\n * \n * return (\n * <div>\n * <input \n * value={query} \n * onChange={(e) => handleSearch(e.target.value)} \n * />\n * <button onClick={handleNextPage}>Next Page</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useSearchParams(): [URLSearchParams, (params: Record<string, string> | URLSearchParams) => void] {\n const context = useContext(RouterContext);\n\n const searchParams = useMemo(() => {\n return new URLSearchParams(context.search);\n }, [context.search]);\n\n const setSearchParams = useMemo(() => {\n return (params: Record<string, string> | URLSearchParams) => {\n const newParams = params instanceof URLSearchParams \n ? params \n : new URLSearchParams(params);\n \n const search = newParams.toString();\n const newUrl = search \n ? `${context.pathname}?${search}` \n : context.pathname;\n \n context.navigate(newUrl, { scroll: false });\n };\n }, [context.pathname, context.navigate]);\n\n return [searchParams, setSearchParams];\n}\n","import { useContext } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Location object returned by useLocation\n */\nexport interface Location {\n /** Current pathname (e.g., /items/123) */\n pathname: string;\n /** Current search string without ? (e.g., page=2&sort=asc) */\n search: string;\n /** Full href */\n href: string;\n /** Parsed search params */\n searchParams: URLSearchParams;\n}\n\n/**\n * Hook to access current location information\n * \n * @returns Location object with pathname, search, href, and searchParams\n * \n * @example\n * ```tsx\n * import { useLocation } from '@riktajs/react';\n * \n * function Breadcrumbs() {\n * const location = useLocation();\n * \n * return (\n * <nav>\n * Current path: {location.pathname}\n * {location.search && <span>?{location.search}</span>}\n * </nav>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Access search params\n * function FilterDisplay() {\n * const { searchParams } = useLocation();\n * const filter = searchParams.get('filter');\n * \n * return filter ? <span>Filtered by: {filter}</span> : null;\n * }\n * ```\n */\nexport function useLocation(): Location {\n const context = useContext(RouterContext);\n\n return {\n pathname: context.pathname,\n search: context.search,\n href: context.href,\n searchParams: new URLSearchParams(context.search),\n };\n}\n","import { useContext } from 'react';\nimport { SsrContext } from '../context/SsrContext.js';\nimport type { SsrData } from '../types.js';\n\n/**\n * Hook to access SSR data passed from server\n * \n * SSR data is passed via window.__SSR_DATA__ and contains\n * the initial data rendered on the server.\n * \n * @returns SSR data object or null if not available\n * \n * @example\n * ```tsx\n * import { useSsrData } from '@riktajs/react';\n * \n * interface PageData {\n * title: string;\n * items: Array<{ id: string; name: string }>;\n * }\n * \n * function ItemList() {\n * const ssrData = useSsrData<PageData>();\n * \n * if (!ssrData) {\n * return <div>Loading...</div>;\n * }\n * \n * return (\n * <div>\n * <h1>{ssrData.data.title}</h1>\n * <ul>\n * {ssrData.data.items.map(item => (\n * <li key={item.id}>{item.name}</li>\n * ))}\n * </ul>\n * </div>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Access just the data\n * function MyComponent() {\n * const ssrData = useSsrData<{ user: User }>();\n * const user = ssrData?.data.user;\n * \n * return user ? <UserProfile user={user} /> : <LoginPrompt />;\n * }\n * ```\n */\nexport function useSsrData<T = unknown>(): SsrData<T> | null {\n const context = useContext(SsrContext);\n return context as SsrData<T> | null;\n}\n","import { useState, useEffect } from 'react';\nimport type { HydrationState } from '../types.js';\n\n/**\n * Hook to track hydration state\n * \n * Useful for rendering different content during SSR vs after hydration,\n * or for avoiding hydration mismatches.\n * \n * @returns Hydration state object\n * \n * @example\n * ```tsx\n * import { useHydration } from '@riktajs/react';\n * \n * function TimeDisplay() {\n * const { isHydrated, isServer } = useHydration();\n * \n * // On server and initial render, show static content\n * // After hydration, show dynamic content\n * if (!isHydrated) {\n * return <span>Loading time...</span>;\n * }\n * \n * return <span>{new Date().toLocaleTimeString()}</span>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Avoid hydration mismatch with client-only content\n * function ClientOnlyComponent() {\n * const { isHydrated } = useHydration();\n * \n * if (!isHydrated) {\n * return null; // Or a placeholder\n * }\n * \n * return <SomeClientOnlyLibrary />;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Conditional rendering based on environment\n * function DebugPanel() {\n * const { isServer } = useHydration();\n * \n * // Never render on server, only after client hydration\n * if (isServer) return null;\n * \n * return <DevTools />;\n * }\n * ```\n */\nexport function useHydration(): HydrationState {\n const isServer = typeof window === 'undefined';\n const [isHydrated, setIsHydrated] = useState(false);\n\n useEffect(() => {\n setIsHydrated(true);\n }, []);\n\n return {\n isHydrated,\n isServer,\n };\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport type { FetchState } from '../types.js';\n\n/**\n * Options for useFetch hook\n */\nexport interface UseFetchOptions extends Omit<RequestInit, 'body'> {\n /** Skip initial fetch (useful for conditional fetching) */\n skip?: boolean;\n /** Dependencies that trigger refetch when changed */\n deps?: unknown[];\n /** Transform response before setting data */\n transform?: (data: unknown) => unknown;\n}\n\n/**\n * Hook for data fetching with loading and error states\n * \n * @param url URL to fetch from\n * @param options Fetch options\n * @returns Fetch state with data, loading, error, and refetch function\n * \n * @example\n * ```tsx\n * import { useFetch } from '@riktajs/react';\n * \n * interface User {\n * id: string;\n * name: string;\n * }\n * \n * function UserProfile({ userId }: { userId: string }) {\n * const { data, loading, error, refetch } = useFetch<User>(\n * `/api/users/${userId}`\n * );\n * \n * if (loading) return <Spinner />;\n * if (error) return <Error message={error} />;\n * if (!data) return null;\n * \n * return (\n * <div>\n * <h1>{data.name}</h1>\n * <button onClick={refetch}>Refresh</button>\n * </div>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * const { data } = useFetch<Item[]>('/api/items', {\n * headers: { 'Authorization': `Bearer ${token}` },\n * deps: [token], // Refetch when token changes\n * skip: !token, // Don't fetch until we have a token\n * });\n * ```\n * \n * @example\n * ```tsx\n * // With transform\n * const { data } = useFetch<{ results: Item[] }>('/api/search', {\n * transform: (res) => res.results, // Extract just the results array\n * });\n * ```\n */\nexport function useFetch<T = unknown>(\n url: string,\n options: UseFetchOptions = {}\n): FetchState<T> {\n const { skip = false, deps = [], transform, ...fetchOptions } = options;\n \n const [state, setState] = useState<Omit<FetchState<T>, 'refetch'>>({\n data: null,\n loading: !skip,\n error: null,\n });\n\n // Use ref to track if component is mounted\n const mountedRef = useRef(true);\n // Use ref to track current fetch to handle race conditions\n const fetchIdRef = useRef(0);\n\n const doFetch = useCallback(async (ignoreSkip = false) => {\n if (skip && !ignoreSkip) return;\n\n const fetchId = ++fetchIdRef.current;\n setState(prev => ({ ...prev, loading: true, error: null }));\n\n try {\n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n let data = await response.json();\n\n // Apply transform if provided\n if (transform) {\n data = transform(data);\n }\n\n // Only update state if this is still the current fetch and component is mounted\n if (fetchId === fetchIdRef.current && mountedRef.current) {\n setState({ data: data as T, loading: false, error: null });\n }\n } catch (err) {\n if (fetchId === fetchIdRef.current && mountedRef.current) {\n const message = err instanceof Error ? err.message : 'An error occurred';\n setState({ data: null, loading: false, error: message });\n }\n }\n }, [url, skip, JSON.stringify(fetchOptions), transform]);\n\n // Fetch on mount and when dependencies change\n useEffect(() => {\n mountedRef.current = true;\n doFetch();\n\n return () => {\n mountedRef.current = false;\n };\n }, [doFetch, ...deps]);\n\n // refetch ignores skip option to allow manual triggering\n const refetch = useCallback(async () => {\n await doFetch(true);\n }, [doFetch]);\n\n return {\n ...state,\n refetch,\n };\n}\n","import { useState, useCallback } from 'react';\nimport type { ActionResult, ActionState } from '../types.js';\n\n/**\n * Options for useAction hook\n */\nexport interface UseActionOptions<TResult = unknown> {\n /** Callback on successful action */\n onSuccess?: (result: TResult) => void;\n /** Callback on action error */\n onError?: (error: string) => void;\n /** HTTP method to use */\n method?: 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n /** Additional headers */\n headers?: Record<string, string>;\n}\n\n/**\n * Hook for executing server actions (form submissions, mutations)\n * \n * @param url URL to send the action to\n * @param options Action options\n * @returns Action state with execute, pending, result, and reset\n * \n * @example\n * ```tsx\n * import { useAction } from '@riktajs/react';\n * \n * interface CreateItemInput {\n * name: string;\n * price: number;\n * }\n * \n * interface Item {\n * id: string;\n * name: string;\n * price: number;\n * }\n * \n * function CreateItemForm() {\n * const { execute, pending, result } = useAction<CreateItemInput, Item>(\n * '/api/items',\n * {\n * onSuccess: (item) => {\n * console.log('Created item:', item);\n * },\n * }\n * );\n * \n * const handleSubmit = async (e: FormEvent) => {\n * e.preventDefault();\n * const formData = new FormData(e.target as HTMLFormElement);\n * await execute({\n * name: formData.get('name') as string,\n * price: Number(formData.get('price')),\n * });\n * };\n * \n * return (\n * <form onSubmit={handleSubmit}>\n * <input name=\"name\" required />\n * <input name=\"price\" type=\"number\" required />\n * <button disabled={pending}>\n * {pending ? 'Creating...' : 'Create Item'}\n * </button>\n * {result?.error && <p className=\"error\">{result.error}</p>}\n * {result?.fieldErrors?.name && (\n * <p className=\"error\">{result.fieldErrors.name[0]}</p>\n * )}\n * </form>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // DELETE action\n * const { execute, pending } = useAction<{ id: string }, void>(\n * '/api/items',\n * { method: 'DELETE' }\n * );\n * \n * const handleDelete = () => execute({ id: itemId });\n * ```\n */\nexport function useAction<TInput = unknown, TResult = unknown>(\n url: string,\n options: UseActionOptions<TResult> = {}\n): ActionState<TInput, TResult> {\n const {\n onSuccess,\n onError,\n method = 'POST',\n headers: customHeaders = {},\n } = options;\n\n const [pending, setPending] = useState(false);\n const [result, setResult] = useState<ActionResult<TResult> | null>(null);\n\n const execute = useCallback(\n async (input: TInput): Promise<ActionResult<TResult>> => {\n setPending(true);\n setResult(null);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...customHeaders,\n },\n body: JSON.stringify(input),\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n // Handle error response\n const actionResult: ActionResult<TResult> = {\n success: false,\n error: data.message || data.error || `HTTP ${response.status}`,\n fieldErrors: data.fieldErrors,\n };\n setResult(actionResult);\n onError?.(actionResult.error!);\n return actionResult;\n }\n\n // Handle success response\n const actionResult: ActionResult<TResult> = {\n success: true,\n data: data as TResult,\n };\n setResult(actionResult);\n onSuccess?.(data as TResult);\n return actionResult;\n } catch (err) {\n const message = err instanceof Error ? err.message : 'An error occurred';\n const actionResult: ActionResult<TResult> = {\n success: false,\n error: message,\n };\n setResult(actionResult);\n onError?.(message);\n return actionResult;\n } finally {\n setPending(false);\n }\n },\n [url, method, JSON.stringify(customHeaders), onSuccess, onError]\n );\n\n const reset = useCallback(() => {\n setResult(null);\n }, []);\n\n return {\n execute,\n pending,\n result,\n reset,\n };\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -10,6 +10,10 @@ interface SsrData<T = unknown> {
|
|
|
10
10
|
data: T;
|
|
11
11
|
/** Current URL path */
|
|
12
12
|
url: string;
|
|
13
|
+
/** Page title (from @Ssr decorator) */
|
|
14
|
+
title?: string;
|
|
15
|
+
/** Page description (from @Ssr decorator) */
|
|
16
|
+
description?: string;
|
|
13
17
|
/** HTTP status code */
|
|
14
18
|
status?: number;
|
|
15
19
|
/** Additional metadata */
|
|
@@ -26,11 +30,13 @@ interface RouterContextValue {
|
|
|
26
30
|
/** Full URL */
|
|
27
31
|
href: string;
|
|
28
32
|
/** Navigate to a new URL */
|
|
29
|
-
navigate: (url: string, options?: NavigateOptions) => void
|
|
33
|
+
navigate: (url: string, options?: NavigateOptions) => void | Promise<void>;
|
|
30
34
|
/** Extracted route params (e.g., { id: '123' } for /item/:id) */
|
|
31
35
|
params: Record<string, string>;
|
|
32
36
|
/** Update route params (used internally by RiktaProvider) */
|
|
33
37
|
setParams: (params: Record<string, string>) => void;
|
|
38
|
+
/** Whether a navigation is in progress */
|
|
39
|
+
isNavigating?: boolean;
|
|
34
40
|
}
|
|
35
41
|
/**
|
|
36
42
|
* Navigation options
|
|
@@ -141,6 +147,7 @@ declare const SsrContext: react.Context<SsrData<unknown> | null>;
|
|
|
141
147
|
* RiktaProvider - Main provider component for Rikta React utilities
|
|
142
148
|
*
|
|
143
149
|
* Provides routing context, SSR data, and navigation utilities to the app.
|
|
150
|
+
* Automatically fetches new page data during client-side navigation.
|
|
144
151
|
*
|
|
145
152
|
* @example
|
|
146
153
|
* ```tsx
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,10 @@ interface SsrData<T = unknown> {
|
|
|
10
10
|
data: T;
|
|
11
11
|
/** Current URL path */
|
|
12
12
|
url: string;
|
|
13
|
+
/** Page title (from @Ssr decorator) */
|
|
14
|
+
title?: string;
|
|
15
|
+
/** Page description (from @Ssr decorator) */
|
|
16
|
+
description?: string;
|
|
13
17
|
/** HTTP status code */
|
|
14
18
|
status?: number;
|
|
15
19
|
/** Additional metadata */
|
|
@@ -26,11 +30,13 @@ interface RouterContextValue {
|
|
|
26
30
|
/** Full URL */
|
|
27
31
|
href: string;
|
|
28
32
|
/** Navigate to a new URL */
|
|
29
|
-
navigate: (url: string, options?: NavigateOptions) => void
|
|
33
|
+
navigate: (url: string, options?: NavigateOptions) => void | Promise<void>;
|
|
30
34
|
/** Extracted route params (e.g., { id: '123' } for /item/:id) */
|
|
31
35
|
params: Record<string, string>;
|
|
32
36
|
/** Update route params (used internally by RiktaProvider) */
|
|
33
37
|
setParams: (params: Record<string, string>) => void;
|
|
38
|
+
/** Whether a navigation is in progress */
|
|
39
|
+
isNavigating?: boolean;
|
|
34
40
|
}
|
|
35
41
|
/**
|
|
36
42
|
* Navigation options
|
|
@@ -141,6 +147,7 @@ declare const SsrContext: react.Context<SsrData<unknown> | null>;
|
|
|
141
147
|
* RiktaProvider - Main provider component for Rikta React utilities
|
|
142
148
|
*
|
|
143
149
|
* Provides routing context, SSR data, and navigation utilities to the app.
|
|
150
|
+
* Automatically fetches new page data during client-side navigation.
|
|
144
151
|
*
|
|
145
152
|
* @example
|
|
146
153
|
* ```tsx
|
package/dist/index.js
CHANGED
|
@@ -11,14 +11,26 @@ var defaultRouterContext = {
|
|
|
11
11
|
},
|
|
12
12
|
params: {},
|
|
13
13
|
setParams: () => {
|
|
14
|
-
}
|
|
14
|
+
},
|
|
15
|
+
isNavigating: false
|
|
15
16
|
};
|
|
16
17
|
var RouterContext = createContext(defaultRouterContext);
|
|
17
18
|
RouterContext.displayName = "RiktaRouterContext";
|
|
18
19
|
var SsrContext = createContext(null);
|
|
19
20
|
SsrContext.displayName = "RiktaSsrContext";
|
|
20
|
-
function getLocationInfo() {
|
|
21
|
+
function getLocationInfo(ssrUrl) {
|
|
21
22
|
if (typeof window === "undefined") {
|
|
23
|
+
if (ssrUrl) {
|
|
24
|
+
try {
|
|
25
|
+
const url = new URL(ssrUrl, "http://localhost");
|
|
26
|
+
return {
|
|
27
|
+
pathname: url.pathname,
|
|
28
|
+
search: url.search.slice(1),
|
|
29
|
+
href: ssrUrl
|
|
30
|
+
};
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
}
|
|
22
34
|
return { pathname: "/", search: "", href: "/" };
|
|
23
35
|
}
|
|
24
36
|
return {
|
|
@@ -30,19 +42,42 @@ function getLocationInfo() {
|
|
|
30
42
|
}
|
|
31
43
|
function getSsrData() {
|
|
32
44
|
if (typeof window === "undefined") return void 0;
|
|
33
|
-
|
|
45
|
+
const rawData = window.__SSR_DATA__;
|
|
46
|
+
if (!rawData) return void 0;
|
|
47
|
+
return rawData;
|
|
48
|
+
}
|
|
49
|
+
async function fetchSsrData(url) {
|
|
50
|
+
try {
|
|
51
|
+
const response = await fetch(url, {
|
|
52
|
+
headers: {
|
|
53
|
+
"X-Rikta-Data": "1",
|
|
54
|
+
"Accept": "application/json"
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
if (response.ok) {
|
|
58
|
+
const data = await response.json();
|
|
59
|
+
return data;
|
|
60
|
+
}
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.warn("[RiktaReact] Failed to fetch SSR data:", error);
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
34
65
|
}
|
|
35
66
|
var RiktaProvider = ({
|
|
36
67
|
ssrData: initialSsrData,
|
|
37
68
|
initialParams = {},
|
|
38
69
|
children
|
|
39
70
|
}) => {
|
|
40
|
-
const [ssrData] = useState(() => {
|
|
71
|
+
const [ssrData, setSsrData] = useState(() => {
|
|
41
72
|
return initialSsrData ?? getSsrData() ?? null;
|
|
42
73
|
});
|
|
43
|
-
const [
|
|
74
|
+
const [isNavigating, setIsNavigating] = useState(false);
|
|
75
|
+
const [location, setLocation] = useState(() => {
|
|
76
|
+
const resolvedSsrData = initialSsrData ?? getSsrData();
|
|
77
|
+
return getLocationInfo(resolvedSsrData?.url);
|
|
78
|
+
});
|
|
44
79
|
const [params, setParams] = useState(initialParams);
|
|
45
|
-
const navigate = useCallback((url, options = {}) => {
|
|
80
|
+
const navigate = useCallback(async (url, options = {}) => {
|
|
46
81
|
const { replace = false, scroll = true, state } = options;
|
|
47
82
|
if (typeof window === "undefined") return;
|
|
48
83
|
let targetUrl;
|
|
@@ -56,6 +91,11 @@ var RiktaProvider = ({
|
|
|
56
91
|
window.location.href = url;
|
|
57
92
|
return;
|
|
58
93
|
}
|
|
94
|
+
setIsNavigating(true);
|
|
95
|
+
const newSsrData = await fetchSsrData(targetUrl.href);
|
|
96
|
+
if (newSsrData) {
|
|
97
|
+
setSsrData(newSsrData);
|
|
98
|
+
}
|
|
59
99
|
if (replace) {
|
|
60
100
|
window.history.replaceState(state ?? null, "", targetUrl.href);
|
|
61
101
|
} else {
|
|
@@ -69,12 +109,19 @@ var RiktaProvider = ({
|
|
|
69
109
|
if (scroll) {
|
|
70
110
|
window.scrollTo(0, 0);
|
|
71
111
|
}
|
|
72
|
-
|
|
112
|
+
setIsNavigating(false);
|
|
73
113
|
}, []);
|
|
74
114
|
useEffect(() => {
|
|
75
115
|
if (typeof window === "undefined") return;
|
|
76
|
-
const handlePopState = () => {
|
|
77
|
-
|
|
116
|
+
const handlePopState = async () => {
|
|
117
|
+
const newLocation = getLocationInfo();
|
|
118
|
+
setIsNavigating(true);
|
|
119
|
+
const newSsrData = await fetchSsrData(newLocation.href);
|
|
120
|
+
if (newSsrData) {
|
|
121
|
+
setSsrData(newSsrData);
|
|
122
|
+
}
|
|
123
|
+
setLocation(newLocation);
|
|
124
|
+
setIsNavigating(false);
|
|
78
125
|
};
|
|
79
126
|
window.addEventListener("popstate", handlePopState);
|
|
80
127
|
return () => window.removeEventListener("popstate", handlePopState);
|
|
@@ -85,8 +132,9 @@ var RiktaProvider = ({
|
|
|
85
132
|
href: location.href,
|
|
86
133
|
navigate,
|
|
87
134
|
params,
|
|
88
|
-
setParams
|
|
89
|
-
|
|
135
|
+
setParams,
|
|
136
|
+
isNavigating
|
|
137
|
+
}), [location.pathname, location.search, location.href, navigate, params, isNavigating]);
|
|
90
138
|
return /* @__PURE__ */ jsx(SsrContext.Provider, { value: ssrData, children: /* @__PURE__ */ jsx(RouterContext.Provider, { value: routerValue, children }) });
|
|
91
139
|
};
|
|
92
140
|
RiktaProvider.displayName = "RiktaProvider";
|
|
@@ -194,8 +242,8 @@ function useFetch(url, options = {}) {
|
|
|
194
242
|
});
|
|
195
243
|
const mountedRef = useRef(true);
|
|
196
244
|
const fetchIdRef = useRef(0);
|
|
197
|
-
const
|
|
198
|
-
if (skip) return;
|
|
245
|
+
const doFetch = useCallback(async (ignoreSkip = false) => {
|
|
246
|
+
if (skip && !ignoreSkip) return;
|
|
199
247
|
const fetchId = ++fetchIdRef.current;
|
|
200
248
|
setState((prev) => ({ ...prev, loading: true, error: null }));
|
|
201
249
|
try {
|
|
@@ -219,14 +267,14 @@ function useFetch(url, options = {}) {
|
|
|
219
267
|
}, [url, skip, JSON.stringify(fetchOptions), transform]);
|
|
220
268
|
useEffect(() => {
|
|
221
269
|
mountedRef.current = true;
|
|
222
|
-
|
|
270
|
+
doFetch();
|
|
223
271
|
return () => {
|
|
224
272
|
mountedRef.current = false;
|
|
225
273
|
};
|
|
226
|
-
}, [
|
|
274
|
+
}, [doFetch, ...deps]);
|
|
227
275
|
const refetch = useCallback(async () => {
|
|
228
|
-
await
|
|
229
|
-
}, [
|
|
276
|
+
await doFetch(true);
|
|
277
|
+
}, [doFetch]);
|
|
230
278
|
return {
|
|
231
279
|
...state,
|
|
232
280
|
refetch
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context/RouterContext.ts","../src/context/SsrContext.ts","../src/components/RiktaProvider.tsx","../src/hooks/useNavigation.ts","../src/components/Link.tsx","../src/hooks/useParams.ts","../src/hooks/useSearchParams.ts","../src/hooks/useLocation.ts","../src/hooks/useSsrData.ts","../src/hooks/useHydration.ts","../src/hooks/useFetch.ts","../src/hooks/useAction.ts"],"names":["createContext","useCallback","jsx","useContext","useMemo","useState","useEffect","actionResult"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext } from 'react';\nimport type { RouterContextValue } from '../types.js';\n\n/**\n * Default router context value for when no provider is present\n */\nconst defaultRouterContext: RouterContextValue = {\n pathname: '/',\n search: '',\n href: '/',\n navigate: () => {\n console.warn('[RiktaReact] Router context not initialized. Wrap your app with <RiktaProvider>');\n },\n params: {},\n setParams: () => {},\n};\n\n/**\n * React context for router state\n * Provides navigation utilities and current location info\n */\nexport const RouterContext = createContext<RouterContextValue>(defaultRouterContext);\n\nRouterContext.displayName = 'RiktaRouterContext';\n","import { createContext } from 'react';\nimport type { SsrData } from '../types.js';\n\n/**\n * React context for SSR data\n * Holds the server-rendered data passed via window.__SSR_DATA__\n */\nexport const SsrContext = createContext<SsrData | null>(null);\n\nSsrContext.displayName = 'RiktaSsrContext';\n","import { useState, useCallback, useEffect, useMemo, type FC } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\nimport { SsrContext } from '../context/SsrContext.js';\nimport type { RiktaProviderProps, SsrData, NavigateOptions } from '../types.js';\n\n/**\n * Get current location from window or fallback for SSR\n */\nfunction getLocationInfo() {\n if (typeof window === 'undefined') {\n return { pathname: '/', search: '', href: '/' };\n }\n return {\n pathname: window.location.pathname,\n search: window.location.search.slice(1), // Remove leading ?\n href: window.location.href,\n };\n}\n\n/**\n * Get SSR data from window\n */\nfunction getSsrData(): SsrData | undefined {\n if (typeof window === 'undefined') return undefined;\n return window.__SSR_DATA__;\n}\n\n/**\n * RiktaProvider - Main provider component for Rikta React utilities\n * \n * Provides routing context, SSR data, and navigation utilities to the app.\n * \n * @example\n * ```tsx\n * // In entry-client.tsx\n * import { RiktaProvider } from '@riktajs/react';\n * \n * hydrateRoot(\n * document.getElementById('root')!,\n * <RiktaProvider>\n * <App />\n * </RiktaProvider>\n * );\n * ```\n */\nexport const RiktaProvider: FC<RiktaProviderProps> = ({\n ssrData: initialSsrData,\n initialParams = {},\n children,\n}) => {\n // Initialize SSR data from window or props\n const [ssrData] = useState<SsrData | null>(() => {\n return initialSsrData ?? getSsrData() ?? null;\n });\n\n // Initialize location state\n const [location, setLocation] = useState(getLocationInfo);\n \n // Route params state\n const [params, setParams] = useState<Record<string, string>>(initialParams);\n\n /**\n * Navigate to a new URL using History API\n */\n const navigate = useCallback((url: string, options: NavigateOptions = {}) => {\n const { replace = false, scroll = true, state } = options;\n\n if (typeof window === 'undefined') return;\n\n // Handle full URLs vs relative paths\n let targetUrl: URL;\n try {\n targetUrl = new URL(url, window.location.origin);\n } catch {\n console.error(`[RiktaReact] Invalid URL: ${url}`);\n return;\n }\n\n // Only handle same-origin navigation\n if (targetUrl.origin !== window.location.origin) {\n window.location.href = url;\n return;\n }\n\n // Update history\n if (replace) {\n window.history.replaceState(state ?? null, '', targetUrl.href);\n } else {\n window.history.pushState(state ?? null, '', targetUrl.href);\n }\n\n // Update location state\n setLocation({\n pathname: targetUrl.pathname,\n search: targetUrl.search.slice(1),\n href: targetUrl.href,\n });\n\n // Scroll to top if requested\n if (scroll) {\n window.scrollTo(0, 0);\n }\n\n // Dispatch popstate event for any other listeners\n window.dispatchEvent(new PopStateEvent('popstate', { state }));\n }, []);\n\n // Listen for popstate (browser back/forward)\n useEffect(() => {\n if (typeof window === 'undefined') return;\n\n const handlePopState = () => {\n setLocation(getLocationInfo());\n };\n\n window.addEventListener('popstate', handlePopState);\n return () => window.removeEventListener('popstate', handlePopState);\n }, []);\n\n // Memoize router context value\n const routerValue = useMemo(() => ({\n pathname: location.pathname,\n search: location.search,\n href: location.href,\n navigate,\n params,\n setParams,\n }), [location.pathname, location.search, location.href, navigate, params]);\n\n return (\n <SsrContext.Provider value={ssrData}>\n <RouterContext.Provider value={routerValue}>\n {children}\n </RouterContext.Provider>\n </SsrContext.Provider>\n );\n};\n\nRiktaProvider.displayName = 'RiktaProvider';\n","import { useContext, useCallback } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\nimport type { NavigateOptions } from '../types.js';\n\n/**\n * Hook for programmatic navigation\n * \n * @returns Object with navigate function and current location info\n * \n * @example\n * ```tsx\n * import { useNavigation } from '@riktajs/react';\n * \n * function MyComponent() {\n * const { navigate, pathname } = useNavigation();\n * \n * const handleSubmit = async () => {\n * await saveData();\n * navigate('/success');\n * };\n * \n * return (\n * <button onClick={handleSubmit}>\n * Submit (current path: {pathname})\n * </button>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * const { navigate } = useNavigation();\n * \n * // Replace history entry (for redirects)\n * navigate('/login', { replace: true });\n * \n * // Don't scroll to top\n * navigate('/next', { scroll: false });\n * \n * // Pass state\n * navigate('/edit', { state: { from: 'list' } });\n * ```\n */\nexport function useNavigation() {\n const context = useContext(RouterContext);\n\n const navigate = useCallback(\n (url: string, options?: NavigateOptions) => {\n context.navigate(url, options);\n },\n [context.navigate]\n );\n\n return {\n /** Navigate to a new URL */\n navigate,\n /** Current pathname */\n pathname: context.pathname,\n /** Current search string (without ?) */\n search: context.search,\n /** Full href */\n href: context.href,\n };\n}\n","import { useCallback, type FC, type MouseEvent } from 'react';\nimport { useNavigation } from '../hooks/useNavigation.js';\nimport type { LinkProps } from '../types.js';\n\n/**\n * Link component for client-side navigation\n * \n * Renders an anchor tag that uses the History API for navigation\n * instead of causing a full page reload.\n * \n * @example\n * ```tsx\n * import { Link } from '@riktajs/react';\n * \n * function Nav() {\n * return (\n * <nav>\n * <Link href=\"/\">Home</Link>\n * <Link href=\"/about\">About</Link>\n * <Link href=\"/items/123\">Item 123</Link>\n * </nav>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * <Link href=\"/dashboard\" replace scroll={false}>\n * Dashboard\n * </Link>\n * ```\n */\nexport const Link: FC<LinkProps> = ({\n href,\n replace = false,\n scroll = true,\n prefetch = false,\n state,\n children,\n onClick,\n ...restProps\n}) => {\n const { navigate } = useNavigation();\n\n const handleClick = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n // Call user's onClick handler if provided\n onClick?.(e);\n\n // Don't handle if default was prevented\n if (e.defaultPrevented) return;\n\n // Don't handle modified clicks (open in new tab, etc.)\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n\n // Don't handle right clicks\n if (e.button !== 0) return;\n\n // Don't handle target=\"_blank\" etc.\n const target = (e.currentTarget as HTMLAnchorElement).target;\n if (target && target !== '_self') return;\n\n // Don't handle external links\n try {\n const url = new URL(href, window.location.origin);\n if (url.origin !== window.location.origin) return;\n } catch {\n return;\n }\n\n // Handle the navigation\n e.preventDefault();\n navigate(href, { replace, scroll, state });\n },\n [href, replace, scroll, state, navigate, onClick]\n );\n\n // Prefetch support could be added here in the future\n // using <link rel=\"prefetch\"> or Intersection Observer\n\n return (\n <a href={href} onClick={handleClick} {...restProps}>\n {children}\n </a>\n );\n};\n\nLink.displayName = 'Link';\n","import { useContext } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Hook to access route parameters\n * \n * Route parameters are extracted from the URL path by the server\n * and passed via SSR data. They're stored in the RouterContext.\n * \n * @returns Object with route parameter values\n * \n * @example\n * ```tsx\n * // For route /item/:id\n * import { useParams } from '@riktajs/react';\n * \n * function ItemPage() {\n * const { id } = useParams<{ id: string }>();\n * \n * return <h1>Item {id}</h1>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Multiple params - /users/:userId/posts/:postId\n * function PostPage() {\n * const { userId, postId } = useParams<{ userId: string; postId: string }>();\n * \n * return <h1>Post {postId} by User {userId}</h1>;\n * }\n * ```\n */\nexport function useParams<T extends Record<string, string> = Record<string, string>>(): T {\n const context = useContext(RouterContext);\n return context.params as T;\n}\n","import { useContext, useMemo } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Hook to access and manipulate URL search parameters\n * \n * @returns Tuple of [URLSearchParams, setSearchParams function]\n * \n * @example\n * ```tsx\n * import { useSearchParams } from '@riktajs/react';\n * \n * function SearchPage() {\n * const [searchParams, setSearchParams] = useSearchParams();\n * const query = searchParams.get('q') ?? '';\n * const page = parseInt(searchParams.get('page') ?? '1', 10);\n * \n * const handleSearch = (newQuery: string) => {\n * setSearchParams({ q: newQuery, page: '1' });\n * };\n * \n * const handleNextPage = () => {\n * setSearchParams({ q: query, page: String(page + 1) });\n * };\n * \n * return (\n * <div>\n * <input \n * value={query} \n * onChange={(e) => handleSearch(e.target.value)} \n * />\n * <button onClick={handleNextPage}>Next Page</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useSearchParams(): [URLSearchParams, (params: Record<string, string> | URLSearchParams) => void] {\n const context = useContext(RouterContext);\n\n const searchParams = useMemo(() => {\n return new URLSearchParams(context.search);\n }, [context.search]);\n\n const setSearchParams = useMemo(() => {\n return (params: Record<string, string> | URLSearchParams) => {\n const newParams = params instanceof URLSearchParams \n ? params \n : new URLSearchParams(params);\n \n const search = newParams.toString();\n const newUrl = search \n ? `${context.pathname}?${search}` \n : context.pathname;\n \n context.navigate(newUrl, { scroll: false });\n };\n }, [context.pathname, context.navigate]);\n\n return [searchParams, setSearchParams];\n}\n","import { useContext } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Location object returned by useLocation\n */\nexport interface Location {\n /** Current pathname (e.g., /items/123) */\n pathname: string;\n /** Current search string without ? (e.g., page=2&sort=asc) */\n search: string;\n /** Full href */\n href: string;\n /** Parsed search params */\n searchParams: URLSearchParams;\n}\n\n/**\n * Hook to access current location information\n * \n * @returns Location object with pathname, search, href, and searchParams\n * \n * @example\n * ```tsx\n * import { useLocation } from '@riktajs/react';\n * \n * function Breadcrumbs() {\n * const location = useLocation();\n * \n * return (\n * <nav>\n * Current path: {location.pathname}\n * {location.search && <span>?{location.search}</span>}\n * </nav>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Access search params\n * function FilterDisplay() {\n * const { searchParams } = useLocation();\n * const filter = searchParams.get('filter');\n * \n * return filter ? <span>Filtered by: {filter}</span> : null;\n * }\n * ```\n */\nexport function useLocation(): Location {\n const context = useContext(RouterContext);\n\n return {\n pathname: context.pathname,\n search: context.search,\n href: context.href,\n searchParams: new URLSearchParams(context.search),\n };\n}\n","import { useContext } from 'react';\nimport { SsrContext } from '../context/SsrContext.js';\nimport type { SsrData } from '../types.js';\n\n/**\n * Hook to access SSR data passed from server\n * \n * SSR data is passed via window.__SSR_DATA__ and contains\n * the initial data rendered on the server.\n * \n * @returns SSR data object or null if not available\n * \n * @example\n * ```tsx\n * import { useSsrData } from '@riktajs/react';\n * \n * interface PageData {\n * title: string;\n * items: Array<{ id: string; name: string }>;\n * }\n * \n * function ItemList() {\n * const ssrData = useSsrData<PageData>();\n * \n * if (!ssrData) {\n * return <div>Loading...</div>;\n * }\n * \n * return (\n * <div>\n * <h1>{ssrData.data.title}</h1>\n * <ul>\n * {ssrData.data.items.map(item => (\n * <li key={item.id}>{item.name}</li>\n * ))}\n * </ul>\n * </div>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Access just the data\n * function MyComponent() {\n * const ssrData = useSsrData<{ user: User }>();\n * const user = ssrData?.data.user;\n * \n * return user ? <UserProfile user={user} /> : <LoginPrompt />;\n * }\n * ```\n */\nexport function useSsrData<T = unknown>(): SsrData<T> | null {\n const context = useContext(SsrContext);\n return context as SsrData<T> | null;\n}\n","import { useState, useEffect } from 'react';\nimport type { HydrationState } from '../types.js';\n\n/**\n * Hook to track hydration state\n * \n * Useful for rendering different content during SSR vs after hydration,\n * or for avoiding hydration mismatches.\n * \n * @returns Hydration state object\n * \n * @example\n * ```tsx\n * import { useHydration } from '@riktajs/react';\n * \n * function TimeDisplay() {\n * const { isHydrated, isServer } = useHydration();\n * \n * // On server and initial render, show static content\n * // After hydration, show dynamic content\n * if (!isHydrated) {\n * return <span>Loading time...</span>;\n * }\n * \n * return <span>{new Date().toLocaleTimeString()}</span>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Avoid hydration mismatch with client-only content\n * function ClientOnlyComponent() {\n * const { isHydrated } = useHydration();\n * \n * if (!isHydrated) {\n * return null; // Or a placeholder\n * }\n * \n * return <SomeClientOnlyLibrary />;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Conditional rendering based on environment\n * function DebugPanel() {\n * const { isServer } = useHydration();\n * \n * // Never render on server, only after client hydration\n * if (isServer) return null;\n * \n * return <DevTools />;\n * }\n * ```\n */\nexport function useHydration(): HydrationState {\n const isServer = typeof window === 'undefined';\n const [isHydrated, setIsHydrated] = useState(false);\n\n useEffect(() => {\n setIsHydrated(true);\n }, []);\n\n return {\n isHydrated,\n isServer,\n };\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport type { FetchState } from '../types.js';\n\n/**\n * Options for useFetch hook\n */\nexport interface UseFetchOptions extends Omit<RequestInit, 'body'> {\n /** Skip initial fetch (useful for conditional fetching) */\n skip?: boolean;\n /** Dependencies that trigger refetch when changed */\n deps?: unknown[];\n /** Transform response before setting data */\n transform?: (data: unknown) => unknown;\n}\n\n/**\n * Hook for data fetching with loading and error states\n * \n * @param url URL to fetch from\n * @param options Fetch options\n * @returns Fetch state with data, loading, error, and refetch function\n * \n * @example\n * ```tsx\n * import { useFetch } from '@riktajs/react';\n * \n * interface User {\n * id: string;\n * name: string;\n * }\n * \n * function UserProfile({ userId }: { userId: string }) {\n * const { data, loading, error, refetch } = useFetch<User>(\n * `/api/users/${userId}`\n * );\n * \n * if (loading) return <Spinner />;\n * if (error) return <Error message={error} />;\n * if (!data) return null;\n * \n * return (\n * <div>\n * <h1>{data.name}</h1>\n * <button onClick={refetch}>Refresh</button>\n * </div>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * const { data } = useFetch<Item[]>('/api/items', {\n * headers: { 'Authorization': `Bearer ${token}` },\n * deps: [token], // Refetch when token changes\n * skip: !token, // Don't fetch until we have a token\n * });\n * ```\n * \n * @example\n * ```tsx\n * // With transform\n * const { data } = useFetch<{ results: Item[] }>('/api/search', {\n * transform: (res) => res.results, // Extract just the results array\n * });\n * ```\n */\nexport function useFetch<T = unknown>(\n url: string,\n options: UseFetchOptions = {}\n): FetchState<T> {\n const { skip = false, deps = [], transform, ...fetchOptions } = options;\n \n const [state, setState] = useState<Omit<FetchState<T>, 'refetch'>>({\n data: null,\n loading: !skip,\n error: null,\n });\n\n // Use ref to track if component is mounted\n const mountedRef = useRef(true);\n // Use ref to track current fetch to handle race conditions\n const fetchIdRef = useRef(0);\n\n const fetchData = useCallback(async () => {\n if (skip) return;\n\n const fetchId = ++fetchIdRef.current;\n setState(prev => ({ ...prev, loading: true, error: null }));\n\n try {\n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n let data = await response.json();\n\n // Apply transform if provided\n if (transform) {\n data = transform(data);\n }\n\n // Only update state if this is still the current fetch and component is mounted\n if (fetchId === fetchIdRef.current && mountedRef.current) {\n setState({ data: data as T, loading: false, error: null });\n }\n } catch (err) {\n if (fetchId === fetchIdRef.current && mountedRef.current) {\n const message = err instanceof Error ? err.message : 'An error occurred';\n setState({ data: null, loading: false, error: message });\n }\n }\n }, [url, skip, JSON.stringify(fetchOptions), transform]);\n\n // Fetch on mount and when dependencies change\n useEffect(() => {\n mountedRef.current = true;\n fetchData();\n\n return () => {\n mountedRef.current = false;\n };\n }, [fetchData, ...deps]);\n\n const refetch = useCallback(async () => {\n await fetchData();\n }, [fetchData]);\n\n return {\n ...state,\n refetch,\n };\n}\n","import { useState, useCallback } from 'react';\nimport type { ActionResult, ActionState } from '../types.js';\n\n/**\n * Options for useAction hook\n */\nexport interface UseActionOptions<TResult = unknown> {\n /** Callback on successful action */\n onSuccess?: (result: TResult) => void;\n /** Callback on action error */\n onError?: (error: string) => void;\n /** HTTP method to use */\n method?: 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n /** Additional headers */\n headers?: Record<string, string>;\n}\n\n/**\n * Hook for executing server actions (form submissions, mutations)\n * \n * @param url URL to send the action to\n * @param options Action options\n * @returns Action state with execute, pending, result, and reset\n * \n * @example\n * ```tsx\n * import { useAction } from '@riktajs/react';\n * \n * interface CreateItemInput {\n * name: string;\n * price: number;\n * }\n * \n * interface Item {\n * id: string;\n * name: string;\n * price: number;\n * }\n * \n * function CreateItemForm() {\n * const { execute, pending, result } = useAction<CreateItemInput, Item>(\n * '/api/items',\n * {\n * onSuccess: (item) => {\n * console.log('Created item:', item);\n * },\n * }\n * );\n * \n * const handleSubmit = async (e: FormEvent) => {\n * e.preventDefault();\n * const formData = new FormData(e.target as HTMLFormElement);\n * await execute({\n * name: formData.get('name') as string,\n * price: Number(formData.get('price')),\n * });\n * };\n * \n * return (\n * <form onSubmit={handleSubmit}>\n * <input name=\"name\" required />\n * <input name=\"price\" type=\"number\" required />\n * <button disabled={pending}>\n * {pending ? 'Creating...' : 'Create Item'}\n * </button>\n * {result?.error && <p className=\"error\">{result.error}</p>}\n * {result?.fieldErrors?.name && (\n * <p className=\"error\">{result.fieldErrors.name[0]}</p>\n * )}\n * </form>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // DELETE action\n * const { execute, pending } = useAction<{ id: string }, void>(\n * '/api/items',\n * { method: 'DELETE' }\n * );\n * \n * const handleDelete = () => execute({ id: itemId });\n * ```\n */\nexport function useAction<TInput = unknown, TResult = unknown>(\n url: string,\n options: UseActionOptions<TResult> = {}\n): ActionState<TInput, TResult> {\n const {\n onSuccess,\n onError,\n method = 'POST',\n headers: customHeaders = {},\n } = options;\n\n const [pending, setPending] = useState(false);\n const [result, setResult] = useState<ActionResult<TResult> | null>(null);\n\n const execute = useCallback(\n async (input: TInput): Promise<ActionResult<TResult>> => {\n setPending(true);\n setResult(null);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...customHeaders,\n },\n body: JSON.stringify(input),\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n // Handle error response\n const actionResult: ActionResult<TResult> = {\n success: false,\n error: data.message || data.error || `HTTP ${response.status}`,\n fieldErrors: data.fieldErrors,\n };\n setResult(actionResult);\n onError?.(actionResult.error!);\n return actionResult;\n }\n\n // Handle success response\n const actionResult: ActionResult<TResult> = {\n success: true,\n data: data as TResult,\n };\n setResult(actionResult);\n onSuccess?.(data as TResult);\n return actionResult;\n } catch (err) {\n const message = err instanceof Error ? err.message : 'An error occurred';\n const actionResult: ActionResult<TResult> = {\n success: false,\n error: message,\n };\n setResult(actionResult);\n onError?.(message);\n return actionResult;\n } finally {\n setPending(false);\n }\n },\n [url, method, JSON.stringify(customHeaders), onSuccess, onError]\n );\n\n const reset = useCallback(() => {\n setResult(null);\n }, []);\n\n return {\n execute,\n pending,\n result,\n reset,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/context/RouterContext.ts","../src/context/SsrContext.ts","../src/components/RiktaProvider.tsx","../src/hooks/useNavigation.ts","../src/components/Link.tsx","../src/hooks/useParams.ts","../src/hooks/useSearchParams.ts","../src/hooks/useLocation.ts","../src/hooks/useSsrData.ts","../src/hooks/useHydration.ts","../src/hooks/useFetch.ts","../src/hooks/useAction.ts"],"names":["createContext","useCallback","jsx","useContext","useMemo","useState","useEffect","actionResult"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext } from 'react';\nimport type { RouterContextValue } from '../types.js';\n\n/**\n * Default router context value for when no provider is present\n */\nconst defaultRouterContext: RouterContextValue = {\n pathname: '/',\n search: '',\n href: '/',\n navigate: () => {\n console.warn('[RiktaReact] Router context not initialized. Wrap your app with <RiktaProvider>');\n },\n params: {},\n setParams: () => {},\n isNavigating: false,\n};\n\n/**\n * React context for router state\n * Provides navigation utilities and current location info\n */\nexport const RouterContext = createContext<RouterContextValue>(defaultRouterContext);\n\nRouterContext.displayName = 'RiktaRouterContext';\n","import { createContext } from 'react';\nimport type { SsrData } from '../types.js';\n\n/**\n * React context for SSR data\n * Holds the server-rendered data passed via window.__SSR_DATA__\n */\nexport const SsrContext = createContext<SsrData | null>(null);\n\nSsrContext.displayName = 'RiktaSsrContext';\n","import { useState, useCallback, useEffect, useMemo, type FC } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\nimport { SsrContext } from '../context/SsrContext.js';\nimport type { RiktaProviderProps, SsrData, NavigateOptions } from '../types.js';\n\n/**\n * Get current location from window or fallback for SSR\n */\nfunction getLocationInfo(ssrUrl?: string) {\n if (typeof window === 'undefined') {\n // During SSR, use the URL from ssrData if available\n if (ssrUrl) {\n try {\n const url = new URL(ssrUrl, 'http://localhost');\n return {\n pathname: url.pathname,\n search: url.search.slice(1),\n href: ssrUrl,\n };\n } catch {\n // Fall through to default\n }\n }\n return { pathname: '/', search: '', href: '/' };\n }\n return {\n pathname: window.location.pathname,\n search: window.location.search.slice(1), // Remove leading ?\n href: window.location.href,\n };\n}\n\n/**\n * Get SSR data from window\n * Server now puts data in normalized format: { data, url, title, description }\n */\nfunction getSsrData(): SsrData | undefined {\n if (typeof window === 'undefined') return undefined;\n \n const rawData = window.__SSR_DATA__ as Record<string, unknown> | undefined;\n if (!rawData) return undefined;\n \n // Data should already be in normalized format from entry-server\n return rawData as unknown as SsrData;\n}\n\n/**\n * Fetch SSR data from server for client-side navigation\n */\nasync function fetchSsrData(url: string): Promise<SsrData | null> {\n try {\n const response = await fetch(url, {\n headers: {\n 'X-Rikta-Data': '1',\n 'Accept': 'application/json',\n },\n });\n \n if (response.ok) {\n const data = await response.json();\n return data as SsrData;\n }\n } catch (error) {\n console.warn('[RiktaReact] Failed to fetch SSR data:', error);\n }\n return null;\n}\n\n/**\n * RiktaProvider - Main provider component for Rikta React utilities\n * \n * Provides routing context, SSR data, and navigation utilities to the app.\n * Automatically fetches new page data during client-side navigation.\n * \n * @example\n * ```tsx\n * // In entry-client.tsx\n * import { RiktaProvider } from '@riktajs/react';\n * \n * hydrateRoot(\n * document.getElementById('root')!,\n * <RiktaProvider>\n * <App />\n * </RiktaProvider>\n * );\n * ```\n */\nexport const RiktaProvider: FC<RiktaProviderProps> = ({\n ssrData: initialSsrData,\n initialParams = {},\n children,\n}) => {\n // Initialize SSR data from window or props\n const [ssrData, setSsrData] = useState<SsrData | null>(() => {\n return initialSsrData ?? getSsrData() ?? null;\n });\n\n // Loading state for navigation\n const [isNavigating, setIsNavigating] = useState(false);\n\n // Initialize location state - use URL from ssrData for SSR consistency\n const [location, setLocation] = useState(() => {\n const resolvedSsrData = initialSsrData ?? getSsrData();\n return getLocationInfo(resolvedSsrData?.url);\n });\n \n // Route params state\n const [params, setParams] = useState<Record<string, string>>(initialParams);\n\n /**\n * Navigate to a new URL using History API with data fetching\n */\n const navigate = useCallback(async (url: string, options: NavigateOptions = {}) => {\n const { replace = false, scroll = true, state } = options;\n\n if (typeof window === 'undefined') return;\n\n // Handle full URLs vs relative paths\n let targetUrl: URL;\n try {\n targetUrl = new URL(url, window.location.origin);\n } catch {\n console.error(`[RiktaReact] Invalid URL: ${url}`);\n return;\n }\n\n // Only handle same-origin navigation\n if (targetUrl.origin !== window.location.origin) {\n window.location.href = url;\n return;\n }\n\n // Start navigation\n setIsNavigating(true);\n\n // Fetch new SSR data from server BEFORE updating anything else\n const newSsrData = await fetchSsrData(targetUrl.href);\n\n // Update SSR data FIRST (before location changes)\n if (newSsrData) {\n setSsrData(newSsrData);\n }\n\n // Update history\n if (replace) {\n window.history.replaceState(state ?? null, '', targetUrl.href);\n } else {\n window.history.pushState(state ?? null, '', targetUrl.href);\n }\n\n // Update location state AFTER data is ready\n setLocation({\n pathname: targetUrl.pathname,\n search: targetUrl.search.slice(1),\n href: targetUrl.href,\n });\n\n // Scroll to top if requested\n if (scroll) {\n window.scrollTo(0, 0);\n }\n\n // End navigation\n setIsNavigating(false);\n }, []);\n\n // Listen for popstate (browser back/forward) and fetch data\n useEffect(() => {\n if (typeof window === 'undefined') return;\n\n const handlePopState = async () => {\n const newLocation = getLocationInfo();\n \n // Fetch data for the new URL FIRST\n setIsNavigating(true);\n const newSsrData = await fetchSsrData(newLocation.href);\n \n // Update SSR data BEFORE location\n if (newSsrData) {\n setSsrData(newSsrData);\n }\n \n // Then update location\n setLocation(newLocation);\n setIsNavigating(false);\n };\n\n window.addEventListener('popstate', handlePopState);\n return () => window.removeEventListener('popstate', handlePopState);\n }, []);\n\n // Memoize router context value\n const routerValue = useMemo(() => ({\n pathname: location.pathname,\n search: location.search,\n href: location.href,\n navigate,\n params,\n setParams,\n isNavigating,\n }), [location.pathname, location.search, location.href, navigate, params, isNavigating]);\n\n return (\n <SsrContext.Provider value={ssrData}>\n <RouterContext.Provider value={routerValue}>\n {children}\n </RouterContext.Provider>\n </SsrContext.Provider>\n );\n};\n\nRiktaProvider.displayName = 'RiktaProvider';\n","import { useContext, useCallback } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\nimport type { NavigateOptions } from '../types.js';\n\n/**\n * Hook for programmatic navigation\n * \n * @returns Object with navigate function and current location info\n * \n * @example\n * ```tsx\n * import { useNavigation } from '@riktajs/react';\n * \n * function MyComponent() {\n * const { navigate, pathname } = useNavigation();\n * \n * const handleSubmit = async () => {\n * await saveData();\n * navigate('/success');\n * };\n * \n * return (\n * <button onClick={handleSubmit}>\n * Submit (current path: {pathname})\n * </button>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * const { navigate } = useNavigation();\n * \n * // Replace history entry (for redirects)\n * navigate('/login', { replace: true });\n * \n * // Don't scroll to top\n * navigate('/next', { scroll: false });\n * \n * // Pass state\n * navigate('/edit', { state: { from: 'list' } });\n * ```\n */\nexport function useNavigation() {\n const context = useContext(RouterContext);\n\n const navigate = useCallback(\n (url: string, options?: NavigateOptions) => {\n context.navigate(url, options);\n },\n [context.navigate]\n );\n\n return {\n /** Navigate to a new URL */\n navigate,\n /** Current pathname */\n pathname: context.pathname,\n /** Current search string (without ?) */\n search: context.search,\n /** Full href */\n href: context.href,\n };\n}\n","import { useCallback, type FC, type MouseEvent } from 'react';\nimport { useNavigation } from '../hooks/useNavigation.js';\nimport type { LinkProps } from '../types.js';\n\n/**\n * Link component for client-side navigation\n * \n * Renders an anchor tag that uses the History API for navigation\n * instead of causing a full page reload.\n * \n * @example\n * ```tsx\n * import { Link } from '@riktajs/react';\n * \n * function Nav() {\n * return (\n * <nav>\n * <Link href=\"/\">Home</Link>\n * <Link href=\"/about\">About</Link>\n * <Link href=\"/items/123\">Item 123</Link>\n * </nav>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * <Link href=\"/dashboard\" replace scroll={false}>\n * Dashboard\n * </Link>\n * ```\n */\nexport const Link: FC<LinkProps> = ({\n href,\n replace = false,\n scroll = true,\n prefetch = false,\n state,\n children,\n onClick,\n ...restProps\n}) => {\n const { navigate } = useNavigation();\n\n const handleClick = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n // Call user's onClick handler if provided\n onClick?.(e);\n\n // Don't handle if default was prevented\n if (e.defaultPrevented) return;\n\n // Don't handle modified clicks (open in new tab, etc.)\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n\n // Don't handle right clicks\n if (e.button !== 0) return;\n\n // Don't handle target=\"_blank\" etc.\n const target = (e.currentTarget as HTMLAnchorElement).target;\n if (target && target !== '_self') return;\n\n // Don't handle external links\n try {\n const url = new URL(href, window.location.origin);\n if (url.origin !== window.location.origin) return;\n } catch {\n return;\n }\n\n // Handle the navigation\n e.preventDefault();\n navigate(href, { replace, scroll, state });\n },\n [href, replace, scroll, state, navigate, onClick]\n );\n\n // Prefetch support could be added here in the future\n // using <link rel=\"prefetch\"> or Intersection Observer\n\n return (\n <a href={href} onClick={handleClick} {...restProps}>\n {children}\n </a>\n );\n};\n\nLink.displayName = 'Link';\n","import { useContext } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Hook to access route parameters\n * \n * Route parameters are extracted from the URL path by the server\n * and passed via SSR data. They're stored in the RouterContext.\n * \n * @returns Object with route parameter values\n * \n * @example\n * ```tsx\n * // For route /item/:id\n * import { useParams } from '@riktajs/react';\n * \n * function ItemPage() {\n * const { id } = useParams<{ id: string }>();\n * \n * return <h1>Item {id}</h1>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Multiple params - /users/:userId/posts/:postId\n * function PostPage() {\n * const { userId, postId } = useParams<{ userId: string; postId: string }>();\n * \n * return <h1>Post {postId} by User {userId}</h1>;\n * }\n * ```\n */\nexport function useParams<T extends Record<string, string> = Record<string, string>>(): T {\n const context = useContext(RouterContext);\n return context.params as T;\n}\n","import { useContext, useMemo } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Hook to access and manipulate URL search parameters\n * \n * @returns Tuple of [URLSearchParams, setSearchParams function]\n * \n * @example\n * ```tsx\n * import { useSearchParams } from '@riktajs/react';\n * \n * function SearchPage() {\n * const [searchParams, setSearchParams] = useSearchParams();\n * const query = searchParams.get('q') ?? '';\n * const page = parseInt(searchParams.get('page') ?? '1', 10);\n * \n * const handleSearch = (newQuery: string) => {\n * setSearchParams({ q: newQuery, page: '1' });\n * };\n * \n * const handleNextPage = () => {\n * setSearchParams({ q: query, page: String(page + 1) });\n * };\n * \n * return (\n * <div>\n * <input \n * value={query} \n * onChange={(e) => handleSearch(e.target.value)} \n * />\n * <button onClick={handleNextPage}>Next Page</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useSearchParams(): [URLSearchParams, (params: Record<string, string> | URLSearchParams) => void] {\n const context = useContext(RouterContext);\n\n const searchParams = useMemo(() => {\n return new URLSearchParams(context.search);\n }, [context.search]);\n\n const setSearchParams = useMemo(() => {\n return (params: Record<string, string> | URLSearchParams) => {\n const newParams = params instanceof URLSearchParams \n ? params \n : new URLSearchParams(params);\n \n const search = newParams.toString();\n const newUrl = search \n ? `${context.pathname}?${search}` \n : context.pathname;\n \n context.navigate(newUrl, { scroll: false });\n };\n }, [context.pathname, context.navigate]);\n\n return [searchParams, setSearchParams];\n}\n","import { useContext } from 'react';\nimport { RouterContext } from '../context/RouterContext.js';\n\n/**\n * Location object returned by useLocation\n */\nexport interface Location {\n /** Current pathname (e.g., /items/123) */\n pathname: string;\n /** Current search string without ? (e.g., page=2&sort=asc) */\n search: string;\n /** Full href */\n href: string;\n /** Parsed search params */\n searchParams: URLSearchParams;\n}\n\n/**\n * Hook to access current location information\n * \n * @returns Location object with pathname, search, href, and searchParams\n * \n * @example\n * ```tsx\n * import { useLocation } from '@riktajs/react';\n * \n * function Breadcrumbs() {\n * const location = useLocation();\n * \n * return (\n * <nav>\n * Current path: {location.pathname}\n * {location.search && <span>?{location.search}</span>}\n * </nav>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Access search params\n * function FilterDisplay() {\n * const { searchParams } = useLocation();\n * const filter = searchParams.get('filter');\n * \n * return filter ? <span>Filtered by: {filter}</span> : null;\n * }\n * ```\n */\nexport function useLocation(): Location {\n const context = useContext(RouterContext);\n\n return {\n pathname: context.pathname,\n search: context.search,\n href: context.href,\n searchParams: new URLSearchParams(context.search),\n };\n}\n","import { useContext } from 'react';\nimport { SsrContext } from '../context/SsrContext.js';\nimport type { SsrData } from '../types.js';\n\n/**\n * Hook to access SSR data passed from server\n * \n * SSR data is passed via window.__SSR_DATA__ and contains\n * the initial data rendered on the server.\n * \n * @returns SSR data object or null if not available\n * \n * @example\n * ```tsx\n * import { useSsrData } from '@riktajs/react';\n * \n * interface PageData {\n * title: string;\n * items: Array<{ id: string; name: string }>;\n * }\n * \n * function ItemList() {\n * const ssrData = useSsrData<PageData>();\n * \n * if (!ssrData) {\n * return <div>Loading...</div>;\n * }\n * \n * return (\n * <div>\n * <h1>{ssrData.data.title}</h1>\n * <ul>\n * {ssrData.data.items.map(item => (\n * <li key={item.id}>{item.name}</li>\n * ))}\n * </ul>\n * </div>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Access just the data\n * function MyComponent() {\n * const ssrData = useSsrData<{ user: User }>();\n * const user = ssrData?.data.user;\n * \n * return user ? <UserProfile user={user} /> : <LoginPrompt />;\n * }\n * ```\n */\nexport function useSsrData<T = unknown>(): SsrData<T> | null {\n const context = useContext(SsrContext);\n return context as SsrData<T> | null;\n}\n","import { useState, useEffect } from 'react';\nimport type { HydrationState } from '../types.js';\n\n/**\n * Hook to track hydration state\n * \n * Useful for rendering different content during SSR vs after hydration,\n * or for avoiding hydration mismatches.\n * \n * @returns Hydration state object\n * \n * @example\n * ```tsx\n * import { useHydration } from '@riktajs/react';\n * \n * function TimeDisplay() {\n * const { isHydrated, isServer } = useHydration();\n * \n * // On server and initial render, show static content\n * // After hydration, show dynamic content\n * if (!isHydrated) {\n * return <span>Loading time...</span>;\n * }\n * \n * return <span>{new Date().toLocaleTimeString()}</span>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Avoid hydration mismatch with client-only content\n * function ClientOnlyComponent() {\n * const { isHydrated } = useHydration();\n * \n * if (!isHydrated) {\n * return null; // Or a placeholder\n * }\n * \n * return <SomeClientOnlyLibrary />;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Conditional rendering based on environment\n * function DebugPanel() {\n * const { isServer } = useHydration();\n * \n * // Never render on server, only after client hydration\n * if (isServer) return null;\n * \n * return <DevTools />;\n * }\n * ```\n */\nexport function useHydration(): HydrationState {\n const isServer = typeof window === 'undefined';\n const [isHydrated, setIsHydrated] = useState(false);\n\n useEffect(() => {\n setIsHydrated(true);\n }, []);\n\n return {\n isHydrated,\n isServer,\n };\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport type { FetchState } from '../types.js';\n\n/**\n * Options for useFetch hook\n */\nexport interface UseFetchOptions extends Omit<RequestInit, 'body'> {\n /** Skip initial fetch (useful for conditional fetching) */\n skip?: boolean;\n /** Dependencies that trigger refetch when changed */\n deps?: unknown[];\n /** Transform response before setting data */\n transform?: (data: unknown) => unknown;\n}\n\n/**\n * Hook for data fetching with loading and error states\n * \n * @param url URL to fetch from\n * @param options Fetch options\n * @returns Fetch state with data, loading, error, and refetch function\n * \n * @example\n * ```tsx\n * import { useFetch } from '@riktajs/react';\n * \n * interface User {\n * id: string;\n * name: string;\n * }\n * \n * function UserProfile({ userId }: { userId: string }) {\n * const { data, loading, error, refetch } = useFetch<User>(\n * `/api/users/${userId}`\n * );\n * \n * if (loading) return <Spinner />;\n * if (error) return <Error message={error} />;\n * if (!data) return null;\n * \n * return (\n * <div>\n * <h1>{data.name}</h1>\n * <button onClick={refetch}>Refresh</button>\n * </div>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With options\n * const { data } = useFetch<Item[]>('/api/items', {\n * headers: { 'Authorization': `Bearer ${token}` },\n * deps: [token], // Refetch when token changes\n * skip: !token, // Don't fetch until we have a token\n * });\n * ```\n * \n * @example\n * ```tsx\n * // With transform\n * const { data } = useFetch<{ results: Item[] }>('/api/search', {\n * transform: (res) => res.results, // Extract just the results array\n * });\n * ```\n */\nexport function useFetch<T = unknown>(\n url: string,\n options: UseFetchOptions = {}\n): FetchState<T> {\n const { skip = false, deps = [], transform, ...fetchOptions } = options;\n \n const [state, setState] = useState<Omit<FetchState<T>, 'refetch'>>({\n data: null,\n loading: !skip,\n error: null,\n });\n\n // Use ref to track if component is mounted\n const mountedRef = useRef(true);\n // Use ref to track current fetch to handle race conditions\n const fetchIdRef = useRef(0);\n\n const doFetch = useCallback(async (ignoreSkip = false) => {\n if (skip && !ignoreSkip) return;\n\n const fetchId = ++fetchIdRef.current;\n setState(prev => ({ ...prev, loading: true, error: null }));\n\n try {\n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n let data = await response.json();\n\n // Apply transform if provided\n if (transform) {\n data = transform(data);\n }\n\n // Only update state if this is still the current fetch and component is mounted\n if (fetchId === fetchIdRef.current && mountedRef.current) {\n setState({ data: data as T, loading: false, error: null });\n }\n } catch (err) {\n if (fetchId === fetchIdRef.current && mountedRef.current) {\n const message = err instanceof Error ? err.message : 'An error occurred';\n setState({ data: null, loading: false, error: message });\n }\n }\n }, [url, skip, JSON.stringify(fetchOptions), transform]);\n\n // Fetch on mount and when dependencies change\n useEffect(() => {\n mountedRef.current = true;\n doFetch();\n\n return () => {\n mountedRef.current = false;\n };\n }, [doFetch, ...deps]);\n\n // refetch ignores skip option to allow manual triggering\n const refetch = useCallback(async () => {\n await doFetch(true);\n }, [doFetch]);\n\n return {\n ...state,\n refetch,\n };\n}\n","import { useState, useCallback } from 'react';\nimport type { ActionResult, ActionState } from '../types.js';\n\n/**\n * Options for useAction hook\n */\nexport interface UseActionOptions<TResult = unknown> {\n /** Callback on successful action */\n onSuccess?: (result: TResult) => void;\n /** Callback on action error */\n onError?: (error: string) => void;\n /** HTTP method to use */\n method?: 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n /** Additional headers */\n headers?: Record<string, string>;\n}\n\n/**\n * Hook for executing server actions (form submissions, mutations)\n * \n * @param url URL to send the action to\n * @param options Action options\n * @returns Action state with execute, pending, result, and reset\n * \n * @example\n * ```tsx\n * import { useAction } from '@riktajs/react';\n * \n * interface CreateItemInput {\n * name: string;\n * price: number;\n * }\n * \n * interface Item {\n * id: string;\n * name: string;\n * price: number;\n * }\n * \n * function CreateItemForm() {\n * const { execute, pending, result } = useAction<CreateItemInput, Item>(\n * '/api/items',\n * {\n * onSuccess: (item) => {\n * console.log('Created item:', item);\n * },\n * }\n * );\n * \n * const handleSubmit = async (e: FormEvent) => {\n * e.preventDefault();\n * const formData = new FormData(e.target as HTMLFormElement);\n * await execute({\n * name: formData.get('name') as string,\n * price: Number(formData.get('price')),\n * });\n * };\n * \n * return (\n * <form onSubmit={handleSubmit}>\n * <input name=\"name\" required />\n * <input name=\"price\" type=\"number\" required />\n * <button disabled={pending}>\n * {pending ? 'Creating...' : 'Create Item'}\n * </button>\n * {result?.error && <p className=\"error\">{result.error}</p>}\n * {result?.fieldErrors?.name && (\n * <p className=\"error\">{result.fieldErrors.name[0]}</p>\n * )}\n * </form>\n * );\n * }\n * ```\n * \n * @example\n * ```tsx\n * // DELETE action\n * const { execute, pending } = useAction<{ id: string }, void>(\n * '/api/items',\n * { method: 'DELETE' }\n * );\n * \n * const handleDelete = () => execute({ id: itemId });\n * ```\n */\nexport function useAction<TInput = unknown, TResult = unknown>(\n url: string,\n options: UseActionOptions<TResult> = {}\n): ActionState<TInput, TResult> {\n const {\n onSuccess,\n onError,\n method = 'POST',\n headers: customHeaders = {},\n } = options;\n\n const [pending, setPending] = useState(false);\n const [result, setResult] = useState<ActionResult<TResult> | null>(null);\n\n const execute = useCallback(\n async (input: TInput): Promise<ActionResult<TResult>> => {\n setPending(true);\n setResult(null);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...customHeaders,\n },\n body: JSON.stringify(input),\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n // Handle error response\n const actionResult: ActionResult<TResult> = {\n success: false,\n error: data.message || data.error || `HTTP ${response.status}`,\n fieldErrors: data.fieldErrors,\n };\n setResult(actionResult);\n onError?.(actionResult.error!);\n return actionResult;\n }\n\n // Handle success response\n const actionResult: ActionResult<TResult> = {\n success: true,\n data: data as TResult,\n };\n setResult(actionResult);\n onSuccess?.(data as TResult);\n return actionResult;\n } catch (err) {\n const message = err instanceof Error ? err.message : 'An error occurred';\n const actionResult: ActionResult<TResult> = {\n success: false,\n error: message,\n };\n setResult(actionResult);\n onError?.(message);\n return actionResult;\n } finally {\n setPending(false);\n }\n },\n [url, method, JSON.stringify(customHeaders), onSuccess, onError]\n );\n\n const reset = useCallback(() => {\n setResult(null);\n }, []);\n\n return {\n execute,\n pending,\n result,\n reset,\n };\n}\n"]}
|
package/package.json
CHANGED