@idealyst/navigation 1.0.97 → 1.0.98
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/package.json +5 -5
- package/src/routing/router.web.tsx +57 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idealyst/navigation",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.98",
|
|
4
4
|
"description": "Cross-platform navigation library for React and React Native",
|
|
5
5
|
"readme": "README.md",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"publish:npm": "npm publish"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
|
-
"@idealyst/components": "^1.0.
|
|
47
|
-
"@idealyst/theme": "^1.0.
|
|
46
|
+
"@idealyst/components": "^1.0.98",
|
|
47
|
+
"@idealyst/theme": "^1.0.98",
|
|
48
48
|
"@react-navigation/bottom-tabs": ">=7.0.0",
|
|
49
49
|
"@react-navigation/drawer": ">=7.0.0",
|
|
50
50
|
"@react-navigation/native": ">=7.0.0",
|
|
@@ -60,10 +60,10 @@
|
|
|
60
60
|
"react-router-dom": ">=6.0.0"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
|
-
"@idealyst/components": "^1.0.
|
|
63
|
+
"@idealyst/components": "^1.0.98",
|
|
64
64
|
"@idealyst/datagrid": "^1.0.93",
|
|
65
65
|
"@idealyst/datepicker": "^1.0.93",
|
|
66
|
-
"@idealyst/theme": "^1.0.
|
|
66
|
+
"@idealyst/theme": "^1.0.98",
|
|
67
67
|
"@types/react": "^19.1.8",
|
|
68
68
|
"@types/react-dom": "^19.1.6",
|
|
69
69
|
"react": "^19.1.0",
|
|
@@ -1,47 +1,91 @@
|
|
|
1
|
-
import React, { useEffect, useState } from 'react'
|
|
2
|
-
import { Routes, Route, useLocation, useParams
|
|
1
|
+
import React, { useEffect, useState, useRef } from 'react'
|
|
2
|
+
import { Routes, Route, useLocation, useParams } from '../router'
|
|
3
3
|
import { DefaultStackLayout } from '../layouts/DefaultStackLayout'
|
|
4
4
|
import { DefaultTabLayout } from '../layouts/DefaultTabLayout'
|
|
5
5
|
import { NavigatorParam, RouteParam, NotFoundComponentProps } from './types'
|
|
6
6
|
import { NavigateParams } from '../context/types'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* Wrapper component for
|
|
9
|
+
* Wrapper component for catch-all routes that:
|
|
10
10
|
* 1. Checks onInvalidRoute handler first
|
|
11
|
-
* 2. If handler returns NavigateParams, redirects
|
|
12
|
-
* 3. If handler returns undefined or no handler, renders notFoundComponent
|
|
11
|
+
* 2. If handler returns NavigateParams, redirects (using absolute path)
|
|
12
|
+
* 3. If handler returns undefined or no handler, renders notFoundComponent (if provided)
|
|
13
13
|
*/
|
|
14
14
|
const NotFoundWrapper = ({
|
|
15
15
|
component: Component,
|
|
16
16
|
onInvalidRoute
|
|
17
17
|
}: {
|
|
18
|
-
component
|
|
18
|
+
component?: React.ComponentType<NotFoundComponentProps>
|
|
19
19
|
onInvalidRoute?: (path: string) => NavigateParams | undefined
|
|
20
20
|
}) => {
|
|
21
21
|
const location = useLocation()
|
|
22
22
|
const params = useParams()
|
|
23
|
-
const navigate = useNavigate()
|
|
24
23
|
const [shouldRender, setShouldRender] = useState(false)
|
|
24
|
+
const redirectingRef = useRef(false)
|
|
25
|
+
const lastPathRef = useRef(location.pathname)
|
|
25
26
|
|
|
26
27
|
useEffect(() => {
|
|
28
|
+
// Reset state if path changed (navigated to a different invalid route)
|
|
29
|
+
if (lastPathRef.current !== location.pathname) {
|
|
30
|
+
lastPathRef.current = location.pathname
|
|
31
|
+
redirectingRef.current = false
|
|
32
|
+
setShouldRender(false)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Prevent multiple redirects
|
|
36
|
+
if (redirectingRef.current) {
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
27
40
|
// Check if handler wants to redirect
|
|
28
41
|
if (onInvalidRoute) {
|
|
29
42
|
const redirectParams = onInvalidRoute(location.pathname)
|
|
30
43
|
if (redirectParams) {
|
|
31
|
-
//
|
|
32
|
-
|
|
44
|
+
// Mark as redirecting to prevent loops
|
|
45
|
+
redirectingRef.current = true
|
|
46
|
+
|
|
47
|
+
// Handler returned NavigateParams - redirect using absolute path
|
|
48
|
+
// Ensure path starts with / for absolute navigation
|
|
49
|
+
let targetPath = redirectParams.path
|
|
50
|
+
if (!targetPath.startsWith('/')) {
|
|
51
|
+
targetPath = `/${targetPath}`
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Substitute any vars in the path
|
|
55
|
+
if (redirectParams.vars) {
|
|
56
|
+
Object.entries(redirectParams.vars).forEach(([key, value]) => {
|
|
57
|
+
targetPath = targetPath.replace(`:${key}`, value)
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Use window.history for truly absolute navigation
|
|
62
|
+
// React Router's navigate can have issues in catch-all contexts
|
|
63
|
+
const replaceMode = redirectParams.replace ?? true
|
|
64
|
+
if (replaceMode) {
|
|
65
|
+
window.history.replaceState(null, '', targetPath)
|
|
66
|
+
} else {
|
|
67
|
+
window.history.pushState(null, '', targetPath)
|
|
68
|
+
}
|
|
69
|
+
// Trigger React Router to sync with the new URL
|
|
70
|
+
window.dispatchEvent(new PopStateEvent('popstate'))
|
|
33
71
|
return
|
|
34
72
|
}
|
|
35
73
|
}
|
|
36
|
-
// No redirect - show the 404 component
|
|
74
|
+
// No redirect - show the 404 component (if provided)
|
|
37
75
|
setShouldRender(true)
|
|
38
|
-
}, [location.pathname, onInvalidRoute
|
|
76
|
+
}, [location.pathname, onInvalidRoute])
|
|
39
77
|
|
|
40
78
|
// Don't render until we've checked the handler
|
|
41
79
|
if (!shouldRender) {
|
|
42
80
|
return null
|
|
43
81
|
}
|
|
44
82
|
|
|
83
|
+
// If no component provided, render nothing (handler-only mode)
|
|
84
|
+
if (!Component) {
|
|
85
|
+
console.warn(`[Navigation] Invalid route "${location.pathname}" - no notFoundComponent configured and onInvalidRoute returned undefined`)
|
|
86
|
+
return null
|
|
87
|
+
}
|
|
88
|
+
|
|
45
89
|
return <Component path={location.pathname} params={params as Record<string, string>} />
|
|
46
90
|
}
|
|
47
91
|
|
|
@@ -99,8 +143,8 @@ const buildRoute = (params: RouteParam, index: number, isNested = false) => {
|
|
|
99
143
|
// Build child routes including catch-all for 404
|
|
100
144
|
const childRoutes = params.routes.map((child, childIndex) => buildRoute(child, childIndex, true));
|
|
101
145
|
|
|
102
|
-
// Add catch-all route
|
|
103
|
-
if (params.notFoundComponent) {
|
|
146
|
+
// Add catch-all route if notFoundComponent or onInvalidRoute is configured
|
|
147
|
+
if (params.notFoundComponent || params.onInvalidRoute) {
|
|
104
148
|
childRoutes.push(
|
|
105
149
|
<Route
|
|
106
150
|
key="__notFound__"
|