@fictjs/router 0.16.0 → 0.17.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fictjs/router",
3
- "version": "0.16.0",
3
+ "version": "0.17.1",
4
4
  "description": "Reactive router for Fict applications",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -35,14 +35,14 @@
35
35
  "src"
36
36
  ],
37
37
  "dependencies": {
38
- "@fictjs/runtime": "0.16.0"
38
+ "@fictjs/runtime": "0.17.1"
39
39
  },
40
40
  "devDependencies": {
41
41
  "jsdom": "^28.1.0",
42
42
  "tsup": "^8.5.1",
43
- "@fictjs/vite-plugin": "0.16.0",
44
- "fict": "0.16.0",
45
- "@fictjs/testing-library": "0.16.0"
43
+ "@fictjs/testing-library": "0.17.1",
44
+ "@fictjs/vite-plugin": "0.17.1",
45
+ "fict": "0.17.1"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "fict": ">=0.3.0"
@@ -76,7 +76,7 @@ export function Router(props: BrowserRouterProps & { children?: FictNode }) {
76
76
 
77
77
  return (
78
78
  <RouterProvider history={history} routes={routes} base={props.base}>
79
- <Routes>{props.children}</Routes>
79
+ <Routes routes={routes}>{props.children}</Routes>
80
80
  </RouterProvider>
81
81
  )
82
82
  }
@@ -91,7 +91,7 @@ export function HashRouter(props: HashRouterProps & { children?: FictNode }) {
91
91
 
92
92
  return (
93
93
  <RouterProvider history={history} routes={routes} base={props.base}>
94
- <Routes>{props.children}</Routes>
94
+ <Routes routes={routes}>{props.children}</Routes>
95
95
  </RouterProvider>
96
96
  )
97
97
  }
@@ -114,7 +114,7 @@ export function MemoryRouter(props: MemoryRouterProps & { children?: FictNode })
114
114
 
115
115
  return (
116
116
  <RouterProvider history={history} routes={routes} base={props.base}>
117
- <Routes>{props.children}</Routes>
117
+ <Routes routes={routes}>{props.children}</Routes>
118
118
  </RouterProvider>
119
119
  )
120
120
  }
@@ -128,7 +128,7 @@ export function StaticRouter(props: StaticRouterProps & { children?: FictNode })
128
128
 
129
129
  return (
130
130
  <RouterProvider history={history} routes={routes} base={props.base}>
131
- <Routes>{props.children}</Routes>
131
+ <Routes routes={routes}>{props.children}</Routes>
132
132
  </RouterProvider>
133
133
  )
134
134
  }
@@ -139,6 +139,7 @@ export function StaticRouter(props: StaticRouterProps & { children?: FictNode })
139
139
 
140
140
  interface RoutesProps {
141
141
  children?: FictNode
142
+ routes?: RouteDefinition[]
142
143
  }
143
144
 
144
145
  /**
@@ -149,7 +150,7 @@ export function Routes(props: RoutesProps) {
149
150
  const parentRoute = useRoute()
150
151
 
151
152
  // Get routes from children
152
- const routes = extractRoutes(props.children)
153
+ const routes = props.routes ?? extractRoutes(props.children)
153
154
 
154
155
  // Compile routes for matching
155
156
  const compiledRoutes = routes.map(r => compileRoute(r))
@@ -178,17 +179,30 @@ export function Routes(props: RoutesProps) {
178
179
  return matchRoutes(branches, relativePath) || []
179
180
  })
180
181
 
181
- // Render the matched routes
182
- const matches = currentMatches()
183
- return <>{matches.length > 0 ? renderMatches(matches, 0) : null}</>
182
+ return <CurrentMatchesView matches={currentMatches} />
184
183
  }
185
184
 
186
185
  // ============================================================================
187
186
  // Route Component
188
187
  // ============================================================================
189
188
 
190
- export function renderMatches(matches: RouteMatch[], index: number): FictNode {
191
- const match = matches[index]!
189
+ interface RenderMatchesProps {
190
+ matches: RouteMatch[]
191
+ index: number
192
+ }
193
+
194
+ interface CurrentMatchesProps {
195
+ matches: RouteMatch[] | (() => RouteMatch[])
196
+ }
197
+
198
+ function CurrentMatchesView(props: CurrentMatchesProps): FictNode {
199
+ const matches = () => readAccessor(props.matches)
200
+
201
+ return <>{matches().length > 0 ? renderMatches(matches(), 0) : null}</>
202
+ }
203
+
204
+ function RenderMatchesView(props: RenderMatchesProps): FictNode {
205
+ const match = props.matches[props.index]!
192
206
  const route = match.route
193
207
  const router = useRouter()
194
208
 
@@ -280,7 +294,10 @@ export function renderMatches(matches: RouteMatch[], index: number): FictNode {
280
294
  match: () => match,
281
295
  data: () => dataState().data,
282
296
  error: () => dataState().error,
283
- outlet: () => (index + 1 < matches.length ? renderMatches(matches, index + 1) : null),
297
+ outlet: () =>
298
+ props.index + 1 < props.matches.length ? (
299
+ <RenderMatchesView matches={props.matches} index={props.index + 1} />
300
+ ) : null,
284
301
  resolvePath: wrapAccessor((to: To) => {
285
302
  const basePath = match.pathname
286
303
  const targetPath = typeof to === 'string' ? to : to.pathname || '/'
@@ -288,32 +305,33 @@ export function renderMatches(matches: RouteMatch[], index: number): FictNode {
288
305
  }),
289
306
  }
290
307
 
291
- // Build the route content with context provider
292
- let content: FictNode = (
308
+ const routeContent: FictNode = (
293
309
  <RouteContext.Provider value={routeContext}>{renderContent()}</RouteContext.Provider>
294
310
  )
295
311
 
296
- // Always wrap with ErrorBoundary if errorElement is defined
297
- if (route.errorElement) {
298
- content = (
299
- <ErrorBoundary
300
- fallback={(err: unknown, reset?: () => void) => (
301
- <RouteErrorContext.Provider value={{ error: err, reset }}>
302
- {route.errorElement}
303
- </RouteErrorContext.Provider>
304
- )}
305
- >
306
- {content}
307
- </ErrorBoundary>
308
- )
309
- }
312
+ const errorBoundaryContent: FictNode = route.errorElement ? (
313
+ <ErrorBoundary
314
+ fallback={(err: unknown, reset?: () => void) => (
315
+ <RouteErrorContext.Provider value={{ error: err, reset }}>
316
+ {route.errorElement}
317
+ </RouteErrorContext.Provider>
318
+ )}
319
+ >
320
+ {routeContent}
321
+ </ErrorBoundary>
322
+ ) : (
323
+ routeContent
324
+ )
310
325
 
311
- // If route has loadingElement and component uses Suspense internally
312
- if (route.loadingElement) {
313
- content = <Suspense fallback={route.loadingElement}>{content}</Suspense>
314
- }
326
+ return route.loadingElement ? (
327
+ <Suspense fallback={route.loadingElement}>{errorBoundaryContent}</Suspense>
328
+ ) : (
329
+ errorBoundaryContent
330
+ )
331
+ }
315
332
 
316
- return content
333
+ export function renderMatches(matches: RouteMatch[], index: number): FictNode {
334
+ return <RenderMatchesView matches={matches} index={index} />
317
335
  }
318
336
 
319
337
  interface RouteJSXProps {