@openwebf/react-router 0.2.0

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 ADDED
@@ -0,0 +1,382 @@
1
+ # @openwebf/react-router
2
+
3
+ A React Router implementation for WebF applications using hybrid history API. This package provides navigation and routing capabilities specifically designed for WebF's hybrid environment, combining web-like history management with Flutter-like navigation patterns.
4
+
5
+ ## Features
6
+
7
+ - 🚀 **Hybrid Navigation**: Seamlessly bridge between WebF's native navigation and React's routing
8
+ - 📱 **Flutter-like API**: Familiar navigation methods like `push`, `pop`, `popUntil`, etc.
9
+ - 🎯 **Type-safe Routing**: Full TypeScript support with type-safe route parameters
10
+ - âš¡ **Performance Optimized**: Pre-rendering support and smart component lifecycle management
11
+ - 🔄 **State Management**: Pass and receive data between routes with ease
12
+ - 📊 **Route Context**: Access route information anywhere in your component tree
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @openwebf/react-router
18
+ # or
19
+ yarn add @openwebf/react-router
20
+ # or
21
+ pnpm add @openwebf/react-router
22
+ ```
23
+
24
+ ### Peer Dependencies
25
+
26
+ This package requires the following peer dependencies:
27
+ - `react` >= 16.8.0
28
+ - `react-dom` >= 16.8.0
29
+ - `@openwebf/webf-enterprise-typings` >= 0.22.0
30
+
31
+ ## Quick Start
32
+
33
+ ```tsx
34
+ import React from 'react';
35
+ import { Routes, Route } from '@openwebf/react-router';
36
+
37
+ // Import your page components
38
+ import HomePage from './pages/Home';
39
+ import AboutPage from './pages/About';
40
+ import ProfilePage from './pages/Profile';
41
+
42
+ function App() {
43
+ return (
44
+ <Routes>
45
+ <Route path="/" element={<HomePage />} />
46
+ <Route path="/about" element={<AboutPage />} />
47
+ <Route path="/profile" element={<ProfilePage />} prerender />
48
+ </Routes>
49
+ );
50
+ }
51
+
52
+ export default App;
53
+ ```
54
+
55
+ ## Core Components
56
+
57
+ ### `<Routes>`
58
+
59
+ The container component that wraps all your routes and provides the routing context.
60
+
61
+ ```tsx
62
+ <Routes>
63
+ {/* Your Route components go here */}
64
+ </Routes>
65
+ ```
66
+
67
+ ### `<Route>`
68
+
69
+ Defines a single route in your application.
70
+
71
+ ```tsx
72
+ <Route
73
+ path="/profile" // Required: The path for this route
74
+ element={<ProfilePage />} // Required: Component to render
75
+ prerender={true} // Optional: Pre-render for better performance
76
+ />
77
+ ```
78
+
79
+ #### Props
80
+
81
+ - `path` (string, required): The path pattern for this route
82
+ - `element` (ReactNode, required): The component to render when this route is active
83
+ - `prerender` (boolean, optional): Whether to pre-render this route for better performance
84
+
85
+ ## Hooks
86
+
87
+ ### `useNavigate()`
88
+
89
+ Returns navigation methods to programmatically navigate between routes.
90
+
91
+ ```tsx
92
+ import { useNavigate } from '@openwebf/react-router';
93
+
94
+ function LoginPage() {
95
+ const { navigate, pop, canPop, popAndPush } = useNavigate();
96
+
97
+ const handleLogin = async () => {
98
+ await loginUser();
99
+
100
+ // Navigate to dashboard
101
+ navigate('/dashboard');
102
+
103
+ // Navigate with state
104
+ navigate('/profile', { state: { from: 'login' } });
105
+
106
+ // Replace current route
107
+ navigate('/home', { replace: true });
108
+
109
+ // Go back
110
+ navigate(-1);
111
+ };
112
+
113
+ const handleCancel = () => {
114
+ if (canPop()) {
115
+ pop(); // Go back
116
+ } else {
117
+ navigate('/'); // Go to home if can't go back
118
+ }
119
+ };
120
+
121
+ return (
122
+ <div>
123
+ <button onClick={handleLogin}>Login</button>
124
+ <button onClick={handleCancel}>Cancel</button>
125
+ </div>
126
+ );
127
+ }
128
+ ```
129
+
130
+ #### Navigation Methods
131
+
132
+ - `navigate(to, options?)`: Navigate to a route
133
+ - `to`: string (path) or number (-1 for back)
134
+ - `options`: { replace?: boolean, state?: any }
135
+ - `pop(result?)`: Go back to the previous route
136
+ - `popUntil(path)`: Pop routes until reaching a specific route
137
+ - `popAndPush(path, state?)`: Pop current route and push a new one
138
+ - `pushAndRemoveUntil(newPath, untilPath, state?)`: Push new route and remove all routes until a specific route
139
+ - `canPop()`: Check if navigation can go back
140
+ - `maybePop(result?)`: Pop if possible, returns boolean
141
+
142
+ ### `useLocation()`
143
+
144
+ Returns the current location object with pathname and state.
145
+
146
+ ```tsx
147
+ import { useLocation } from '@openwebf/react-router';
148
+
149
+ function ProfilePage() {
150
+ const location = useLocation();
151
+
152
+ console.log('Current path:', location.pathname);
153
+ console.log('Location state:', location.state);
154
+ console.log('Is active:', location.isActive);
155
+
156
+ return (
157
+ <div>
158
+ <h1>Profile Page</h1>
159
+ {location.state?.from && (
160
+ <p>You came from: {location.state.from}</p>
161
+ )}
162
+ </div>
163
+ );
164
+ }
165
+ ```
166
+
167
+ ### `useRouteContext()`
168
+
169
+ Access detailed route context information.
170
+
171
+ ```tsx
172
+ import { useRouteContext } from '@openwebf/react-router';
173
+
174
+ function MyComponent() {
175
+ const { path, params, isActive, activePath, routeEventKind } = useRouteContext();
176
+
177
+ if (isActive) {
178
+ console.log('This route is currently active');
179
+ console.log('Route params:', params);
180
+ }
181
+
182
+ return <div>Current path: {path}</div>;
183
+ }
184
+ ```
185
+
186
+ ### `useRoutes()`
187
+
188
+ Create routes from a configuration object.
189
+
190
+ ```tsx
191
+ import { useRoutes } from '@openwebf/react-router';
192
+
193
+ function App() {
194
+ const routes = useRoutes([
195
+ { path: '/', element: <Home /> },
196
+ { path: '/about', element: <About /> },
197
+ { path: '/profile', element: <Profile />, prerender: true },
198
+ { path: '/settings', element: <Settings /> }
199
+ ]);
200
+
201
+ return routes;
202
+ }
203
+ ```
204
+
205
+ ## Advanced Usage
206
+
207
+ ### Passing State Between Routes
208
+
209
+ ```tsx
210
+ // Navigate with state
211
+ const { navigate } = useNavigate();
212
+ navigate('/details', {
213
+ state: {
214
+ productId: 123,
215
+ from: 'catalog'
216
+ }
217
+ });
218
+
219
+ // Access state in the target component
220
+ function DetailsPage() {
221
+ const location = useLocation();
222
+ const { productId, from } = location.state || {};
223
+
224
+ return (
225
+ <div>
226
+ <h1>Product {productId}</h1>
227
+ <p>You came from: {from}</p>
228
+ </div>
229
+ );
230
+ }
231
+ ```
232
+
233
+ ### Pre-rendering Routes
234
+
235
+ Pre-rendering improves performance by rendering routes before they're navigated to:
236
+
237
+ ```tsx
238
+ <Routes>
239
+ <Route path="/" element={<Home />} />
240
+ <Route path="/dashboard" element={<Dashboard />} prerender />
241
+ <Route path="/profile" element={<Profile />} prerender />
242
+ </Routes>
243
+ ```
244
+
245
+ ### Programmatic Navigation Patterns
246
+
247
+ ```tsx
248
+ function NavigationExamples() {
249
+ const nav = useNavigate();
250
+
251
+ // Simple navigation
252
+ const goToHome = () => nav.navigate('/');
253
+
254
+ // Replace current entry
255
+ const replaceWithLogin = () => nav.navigate('/login', { replace: true });
256
+
257
+ // Navigate with complex state
258
+ const goToProduct = (product) => {
259
+ nav.navigate(`/products/${product.id}`, {
260
+ state: {
261
+ product,
262
+ timestamp: Date.now()
263
+ }
264
+ });
265
+ };
266
+
267
+ // Complex navigation flow
268
+ const completeCheckout = async () => {
269
+ // Process payment...
270
+ await processPayment();
271
+
272
+ // Go to success page and prevent going back to checkout
273
+ await nav.pushAndRemoveUntil('/success', '/');
274
+ };
275
+
276
+ // Conditional navigation
277
+ const smartBack = () => {
278
+ if (nav.canPop()) {
279
+ nav.pop();
280
+ } else {
281
+ nav.navigate('/');
282
+ }
283
+ };
284
+ }
285
+ ```
286
+
287
+ ## WebF Router API
288
+
289
+ The package exports a `WebFRouter` object that provides low-level access to the routing system:
290
+
291
+ ```tsx
292
+ import { WebFRouter } from '@openwebf/react-router';
293
+
294
+ // Get current route info
295
+ console.log('Current path:', WebFRouter.path);
296
+ console.log('Current state:', WebFRouter.state);
297
+
298
+ // Navigation methods
299
+ WebFRouter.push('/new-route', { data: 'value' });
300
+ WebFRouter.replace('/replacement-route');
301
+ WebFRouter.back();
302
+ WebFRouter.pop();
303
+ WebFRouter.popUntil('/target');
304
+ ```
305
+
306
+ ## TypeScript Support
307
+
308
+ This package is written in TypeScript and provides complete type definitions. Route state can be typed for better type safety:
309
+
310
+ ```tsx
311
+ interface ProfileState {
312
+ userId: string;
313
+ referrer?: string;
314
+ }
315
+
316
+ // Navigate with typed state
317
+ const { navigate } = useNavigate();
318
+ navigate('/profile', {
319
+ state: {
320
+ userId: '123',
321
+ referrer: 'dashboard'
322
+ } as ProfileState
323
+ });
324
+
325
+ // Access typed state
326
+ function ProfilePage() {
327
+ const location = useLocation();
328
+ const state = location.state as ProfileState | undefined;
329
+
330
+ if (state?.userId) {
331
+ // TypeScript knows state.userId is a string
332
+ }
333
+ }
334
+ ```
335
+
336
+ ## Best Practices
337
+
338
+ 1. **Use Pre-rendering for Heavy Components**: Enable `prerender` for routes with expensive initial renders
339
+ 2. **Clean Up State**: Be mindful of state passed between routes, especially for sensitive data
340
+ 3. **Handle Missing State**: Always provide fallbacks when accessing route state
341
+ 4. **Use Type Safety**: Leverage TypeScript for route paths and state objects
342
+ 5. **Avoid Deep Nesting**: Keep your route structure flat when possible
343
+
344
+ ## Migration from React Router
345
+
346
+ If you're migrating from standard React Router, here are the key differences:
347
+
348
+ 1. **Navigation API**: Use `navigate()` instead of `history.push()`
349
+ 2. **Route State**: State is passed differently and accessed via `useLocation()`
350
+ 3. **No Nested Routes**: Current version doesn't support nested routes
351
+ 4. **Flutter-like Methods**: Additional navigation methods like `popUntil`, `popAndPush`
352
+
353
+ ## Examples
354
+
355
+ Check out the [examples](./examples) directory for complete working examples:
356
+
357
+ - [Basic App](./examples/basic/App.tsx) - Simple routing setup
358
+ - [Navigation Example](./examples/basic/NavigationExample.tsx) - Advanced navigation patterns
359
+ - [WebF Pattern](./examples/basic/AppWebFPattern.tsx) - WebF-specific patterns
360
+ - [useRoutes Hook](./examples/basic/UseRoutesExample.tsx) - Configuration-based routing
361
+
362
+ ## Troubleshooting
363
+
364
+ ### Routes not updating
365
+
366
+ Ensure your `Routes` component is at the root of your component tree and not inside any conditional rendering.
367
+
368
+ ### State is undefined
369
+
370
+ Route state is only available when navigating TO a route. It's not persisted across page refreshes in WebF.
371
+
372
+ ### Performance issues
373
+
374
+ Enable `prerender` for routes that take time to render initially.
375
+
376
+ ## Contributing
377
+
378
+ Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.
379
+
380
+ ## License
381
+
382
+ Apache-2.0 License. See [LICENSE](LICENSE) for details.
@@ -0,0 +1,331 @@
1
+ import React from 'react';
2
+
3
+ type RoutePath = string;
4
+ /**
5
+ * WebF Router object - provides comprehensive navigation APIs
6
+ * Combines web-like history management with Flutter-like navigation patterns
7
+ */
8
+ declare const WebFRouter: {
9
+ /**
10
+ * Get the current state object associated with the history entry
11
+ */
12
+ readonly state: any;
13
+ /**
14
+ * Get the current route path
15
+ */
16
+ readonly path: RoutePath;
17
+ /**
18
+ * Navigate to a specified route
19
+ * Applies route guards for permission checks before navigation
20
+ */
21
+ push: <P extends RoutePath>(path: P, state?: any) => Promise<void>;
22
+ /**
23
+ * Replace the current route without adding to history
24
+ * Applies route guards for permission checks before navigation
25
+ */
26
+ replace: <P extends RoutePath>(path: P, state?: any) => Promise<void>;
27
+ /**
28
+ * Navigate back to the previous route
29
+ */
30
+ back: () => void;
31
+ /**
32
+ * Close the current screen and return to the previous one
33
+ * Flutter-style navigation method
34
+ */
35
+ pop: (result?: any) => void;
36
+ /**
37
+ * Pop routes until reaching a specific route
38
+ */
39
+ popUntil: (path: RoutePath) => void;
40
+ /**
41
+ * Pop the current route and push a new named route
42
+ */
43
+ popAndPushNamed: <T extends RoutePath>(path: T, state?: any) => Promise<void>;
44
+ /**
45
+ * Push a new route and remove routes until reaching a specific route
46
+ */
47
+ pushNamedAndRemoveUntil: <T extends RoutePath>(path: T, state: any, untilPath: RoutePath) => Promise<void>;
48
+ /**
49
+ * Push a new route and remove all routes until a specific route (Flutter-style)
50
+ */
51
+ pushNamedAndRemoveUntilRoute: <T extends RoutePath>(newPath: T, untilPath: RoutePath, state?: any) => Promise<void>;
52
+ /**
53
+ * Check if the navigator can go back
54
+ */
55
+ canPop: () => boolean;
56
+ /**
57
+ * Pop the current route if possible
58
+ * Returns true if the pop was successful, false otherwise
59
+ */
60
+ maybePop: (result?: any) => boolean;
61
+ /**
62
+ * Push a new state to the history stack (web-style navigation)
63
+ */
64
+ pushState: (state: any, name: string) => void;
65
+ /**
66
+ * Replace the current history entry with a new one (web-style navigation)
67
+ */
68
+ replaceState: (state: any, name: string) => void;
69
+ /**
70
+ * Pop and push with restoration capability
71
+ * Returns a restoration ID string
72
+ */
73
+ restorablePopAndPushState: (state: any, name: string) => string;
74
+ /**
75
+ * Pop and push named route with restoration capability
76
+ * Returns a restoration ID string
77
+ */
78
+ restorablePopAndPushNamed: <T extends RoutePath>(path: T, state?: any) => Promise<string>;
79
+ };
80
+
81
+ /**
82
+ * Route Component
83
+ *
84
+ * This component is a core part of the application routing system, responsible for:
85
+ * 1. Managing page rendering and lifecycle
86
+ * 2. Providing route context (RouteContext)
87
+ * 3. Handling page navigation bar (AppBar)
88
+ * 4. Providing page lifecycle hooks
89
+ */
90
+
91
+ /**
92
+ * Route component props interface
93
+ */
94
+ interface RouteProps {
95
+ /**
96
+ * Page title
97
+ * Displayed in the center of the navigation bar
98
+ */
99
+ title?: string;
100
+ /**
101
+ * Page path
102
+ * Must be a member of the RoutePath enum
103
+ */
104
+ path: string;
105
+ /**
106
+ * Whether to pre-render
107
+ * If true, the page will be rendered when the app starts, rather than waiting for route navigation
108
+ * Can be used to improve page switching performance or preload data
109
+ *
110
+ * @default false
111
+ */
112
+ prerender?: boolean;
113
+ /**
114
+ * Page content
115
+ * The actual page component to render
116
+ */
117
+ element: React.ReactNode;
118
+ }
119
+ /**
120
+ * Route Component
121
+ *
122
+ * Responsible for managing page rendering, lifecycle and navigation bar
123
+ */
124
+ declare function Route({ path, prerender, element }: RouteProps): React.JSX.Element;
125
+
126
+ /**
127
+ * Hook to get route context
128
+ *
129
+ * Use generic parameters to specify the route path and automatically infer the corresponding state type
130
+ *
131
+ * @template T - Route path type, must be a member of the RoutePath enum
132
+ * @returns Type-safe route context object
133
+ *
134
+ * @example
135
+ * ```tsx
136
+ * const { params, path, isActive } = useRouteContext();
137
+ * ```
138
+ */
139
+ declare function useRouteContext(): {
140
+ isActive: boolean;
141
+ /**
142
+ * Page path
143
+ * Current route path, corresponds to RoutePath enum
144
+ */
145
+ path: string | undefined;
146
+ /**
147
+ * Page state
148
+ * State data passed during route navigation
149
+ */
150
+ params: any;
151
+ /**
152
+ * Current active path from router
153
+ */
154
+ activePath: string | undefined;
155
+ /**
156
+ * Route event kind
157
+ */
158
+ routeEventKind?: "didPushNext" | "didPush" | "didPop" | "didPupNext";
159
+ };
160
+ /**
161
+ * Location object interface
162
+ */
163
+ interface Location {
164
+ /**
165
+ * The path of the current location
166
+ */
167
+ pathname: string;
168
+ /**
169
+ * The state object associated with this location
170
+ */
171
+ state: any;
172
+ /**
173
+ * A unique key for this location
174
+ */
175
+ key?: string;
176
+ }
177
+ /**
178
+ * Hook to get the current location
179
+ *
180
+ * @returns Current location object with pathname and state
181
+ *
182
+ * @example
183
+ * ```tsx
184
+ * function MyComponent() {
185
+ * const location = useLocation();
186
+ *
187
+ * console.log('Current path:', location.pathname);
188
+ * console.log('Location state:', location.state);
189
+ * console.log('Is active:', location.isActive);
190
+ *
191
+ * return <div>Current path: {location.pathname}</div>;
192
+ * }
193
+ * ```
194
+ */
195
+ declare function useLocation(): Location & {
196
+ isActive: boolean;
197
+ };
198
+ /**
199
+ * Route configuration object
200
+ */
201
+ interface RouteObject {
202
+ /**
203
+ * Path for the route
204
+ */
205
+ path: string;
206
+ /**
207
+ * Element to render for this route
208
+ */
209
+ element: React.ReactNode;
210
+ /**
211
+ * Whether to pre-render this route
212
+ */
213
+ prerender?: boolean;
214
+ /**
215
+ * Child routes (not supported yet)
216
+ */
217
+ children?: RouteObject[];
218
+ }
219
+ /**
220
+ * Props for the Routes component
221
+ */
222
+ interface RoutesProps {
223
+ /**
224
+ * Route components as children
225
+ */
226
+ children: React.ReactNode;
227
+ }
228
+ declare function Routes({ children }: RoutesProps): React.JSX.Element;
229
+ /**
230
+ * Hook to create routes from a configuration object
231
+ *
232
+ * @param routes Array of route configuration objects
233
+ * @returns React element tree of Routes and Route components
234
+ *
235
+ * @example
236
+ * ```tsx
237
+ * function App() {
238
+ * const routes = useRoutes([
239
+ * { path: '/', element: <Home /> },
240
+ * { path: '/about', element: <About /> },
241
+ * { path: '/users', element: <Users /> },
242
+ * { path: '/contact', element: <Contact /> }
243
+ * ]);
244
+ *
245
+ * return routes;
246
+ * }
247
+ * ```
248
+ */
249
+ declare function useRoutes(routes: RouteObject[]): React.ReactElement | null;
250
+ /**
251
+ * Navigation function type
252
+ */
253
+ interface NavigateFunction {
254
+ (to: string, options?: NavigateOptions): void;
255
+ (delta: number): void;
256
+ }
257
+ /**
258
+ * Navigation options
259
+ */
260
+ interface NavigateOptions {
261
+ replace?: boolean;
262
+ state?: any;
263
+ }
264
+ /**
265
+ * Extended navigation object with additional methods
266
+ */
267
+ interface NavigationMethods {
268
+ /**
269
+ * Navigate to a route or go back
270
+ */
271
+ navigate: NavigateFunction;
272
+ /**
273
+ * Close the current screen and return to the previous one
274
+ */
275
+ pop: (result?: any) => void;
276
+ /**
277
+ * Pop routes until reaching a specific route
278
+ */
279
+ popUntil: (path: string) => void;
280
+ /**
281
+ * Pop the current route and push a new route
282
+ */
283
+ popAndPush: (path: string, state?: any) => Promise<void>;
284
+ /**
285
+ * Push a new route and remove all routes until a specific route
286
+ */
287
+ pushAndRemoveUntil: (newPath: string, untilPath: string, state?: any) => Promise<void>;
288
+ /**
289
+ * Check if the navigator can go back
290
+ */
291
+ canPop: () => boolean;
292
+ /**
293
+ * Pop the current route if possible
294
+ */
295
+ maybePop: (result?: any) => boolean;
296
+ }
297
+ /**
298
+ * Hook to navigate between routes programmatically
299
+ *
300
+ * @example
301
+ * ```tsx
302
+ * function LoginPage() {
303
+ * const { navigate, pop, canPop } = useNavigate();
304
+ *
305
+ * const handleLogin = async () => {
306
+ * await login();
307
+ * navigate('/dashboard');
308
+ * };
309
+ *
310
+ * const handleReplace = () => {
311
+ * navigate('/home', { replace: true });
312
+ * };
313
+ *
314
+ * const handleWithState = () => {
315
+ * navigate('/profile', { state: { from: 'login' } });
316
+ * };
317
+ *
318
+ * const goBack = () => {
319
+ * if (canPop()) {
320
+ * pop();
321
+ * } else {
322
+ * navigate('/');
323
+ * }
324
+ * };
325
+ * }
326
+ * ```
327
+ */
328
+ declare function useNavigate(): NavigationMethods;
329
+
330
+ export { Route, Routes, WebFRouter, useLocation, useNavigate, useRouteContext, useRoutes };
331
+ export type { Location, NavigateFunction, NavigateOptions, NavigationMethods, RouteObject, RouteProps, RoutesProps };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA"}