@tanstack/react-router 0.0.1-alpha.1 → 0.0.1-alpha.11

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,7 +1,7 @@
1
1
  {
2
2
  "name": "@tanstack/react-router",
3
3
  "author": "Tanner Linsley",
4
- "version": "0.0.1-alpha.1",
4
+ "version": "0.0.1-alpha.11",
5
5
  "license": "MIT",
6
6
  "repository": "tanstack/router",
7
7
  "homepage": "https://react-router.tanstack.com/",
@@ -31,7 +31,7 @@
31
31
  "node": ">=12"
32
32
  },
33
33
  "files": [
34
- "build",
34
+ "build/**",
35
35
  "src"
36
36
  ],
37
37
  "peerDependencies": {
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "@babel/runtime": "^7.16.7",
43
- "@tanstack/router-core": "0.0.1-alpha.1",
43
+ "@tanstack/router-core": "0.0.1-alpha.11",
44
44
  "use-sync-external-store": "^1.2.0"
45
45
  },
46
46
  "devDependencies": {
package/src/index.tsx CHANGED
@@ -4,10 +4,15 @@ import { useSyncExternalStore } from 'use-sync-external-store/shim'
4
4
 
5
5
  import {
6
6
  AnyRoute,
7
+ CheckId,
8
+ CheckPath,
9
+ Expand,
10
+ resolvePath,
7
11
  RootRouteId,
8
12
  rootRouteId,
9
- Route,
10
13
  Router,
14
+ RouterState,
15
+ ToIdOption,
11
16
  } from '@tanstack/router-core'
12
17
  import {
13
18
  warning,
@@ -29,6 +34,7 @@ import {
29
34
  ResolveRelativePath,
30
35
  NoInfer,
31
36
  ToOptions,
37
+ invariant,
32
38
  } from '@tanstack/router-core'
33
39
 
34
40
  export * from '@tanstack/router-core'
@@ -36,59 +42,68 @@ export * from '@tanstack/router-core'
36
42
  declare module '@tanstack/router-core' {
37
43
  interface FrameworkGenerics {
38
44
  Element: React.ReactNode
39
- AsyncElement: (opts: {
40
- params: Record<string, string>
41
- }) => Promise<React.ReactNode>
42
- SyncOrAsyncElement: React.ReactNode | FrameworkGenerics['AsyncElement']
45
+ // Any is required here so import() will work without having to do import().then(d => d.default)
46
+ SyncOrAsyncElement: React.ReactNode | (() => Promise<any>)
43
47
  }
44
48
 
45
49
  interface Router<
46
50
  TRouteConfig extends AnyRouteConfig = RouteConfig,
47
51
  TAllRouteInfo extends AnyAllRouteInfo = AllRouteInfo<TRouteConfig>,
48
- > extends Pick<
49
- Route<TAllRouteInfo, TAllRouteInfo['routeInfoById'][RootRouteId]>,
50
- 'linkProps' | 'Link' | 'MatchRoute'
51
- > {
52
+ > {
53
+ useState: () => RouterState
52
54
  useRoute: <TId extends keyof TAllRouteInfo['routeInfoById']>(
53
55
  routeId: TId,
54
56
  ) => Route<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TId]>
55
57
  useMatch: <TId extends keyof TAllRouteInfo['routeInfoById']>(
56
58
  routeId: TId,
57
59
  ) => RouteMatch<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TId]>
58
- // linkProps: <TTo extends string = '.'>(
59
- // props: LinkPropsOptions<TAllRouteInfo, '/', TTo> &
60
- // React.AnchorHTMLAttributes<HTMLAnchorElement>,
61
- // ) => React.AnchorHTMLAttributes<HTMLAnchorElement>
62
- // Link: <TTo extends string = '.'>(
63
- // props: LinkPropsOptions<TAllRouteInfo, '/', TTo> &
64
- // React.AnchorHTMLAttributes<HTMLAnchorElement> &
65
- // Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'children'> & {
66
- // // If a function is passed as a child, it will be given the `isActive` boolean to aid in further styling on the element it returns
67
- // children?:
68
- // | React.ReactNode
69
- // | ((state: { isActive: boolean }) => React.ReactNode)
70
- // },
71
- // ) => JSX.Element
72
- // MatchRoute: <TTo extends string = '.'>(
73
- // props: ToOptions<TAllRouteInfo, '/', TTo> &
74
- // MatchRouteOptions & {
75
- // // If a function is passed as a child, it will be given the `isActive` boolean to aid in further styling on the element it returns
76
- // children?:
77
- // | React.ReactNode
78
- // | ((
79
- // params: RouteInfoByPath<
80
- // TAllRouteInfo,
81
- // ResolveRelativePath<'/', NoInfer<TTo>>
82
- // >['allParams'],
83
- // ) => React.ReactNode)
84
- // },
85
- // ) => JSX.Element
60
+ linkProps: <TTo extends string = '.'>(
61
+ props: LinkPropsOptions<TAllRouteInfo, '/', TTo> &
62
+ React.AnchorHTMLAttributes<HTMLAnchorElement>,
63
+ ) => React.AnchorHTMLAttributes<HTMLAnchorElement>
64
+ Link: <TTo extends string = '.'>(
65
+ props: LinkPropsOptions<TAllRouteInfo, '/', TTo> &
66
+ React.AnchorHTMLAttributes<HTMLAnchorElement> &
67
+ Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'children'> & {
68
+ // If a function is passed as a child, it will be given the `isActive` boolean to aid in further styling on the element it returns
69
+ children?:
70
+ | React.ReactNode
71
+ | ((state: { isActive: boolean }) => React.ReactNode)
72
+ },
73
+ ) => JSX.Element
74
+ MatchRoute: <TTo extends string = '.'>(
75
+ props: ToOptions<TAllRouteInfo, '/', TTo> &
76
+ MatchRouteOptions & {
77
+ // If a function is passed as a child, it will be given the `isActive` boolean to aid in further styling on the element it returns
78
+ children?:
79
+ | React.ReactNode
80
+ | ((
81
+ params: RouteInfoByPath<
82
+ TAllRouteInfo,
83
+ ResolveRelativePath<'/', NoInfer<TTo>>
84
+ >['allParams'],
85
+ ) => React.ReactNode)
86
+ },
87
+ ) => JSX.Element
86
88
  }
87
89
 
88
90
  interface Route<
89
91
  TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
90
92
  TRouteInfo extends AnyRouteInfo = RouteInfo,
91
93
  > {
94
+ useRoute: <
95
+ TTo extends string = '.',
96
+ TResolved extends string = ResolveRelativePath<
97
+ TRouteInfo['id'],
98
+ NoInfer<TTo>
99
+ >,
100
+ >(
101
+ routeId: CheckId<
102
+ TAllRouteInfo,
103
+ TResolved,
104
+ ToIdOption<TAllRouteInfo, TRouteInfo['id'], TTo>
105
+ >,
106
+ ) => Route<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TResolved]>
92
107
  linkProps: <TTo extends string = '.'>(
93
108
  props: LinkPropsOptions<TAllRouteInfo, TRouteInfo['fullPath'], TTo> &
94
109
  React.AnchorHTMLAttributes<HTMLAnchorElement>,
@@ -174,203 +189,228 @@ const useRouterSubscription = (router: Router<any, any>) => {
174
189
  export function createReactRouter<
175
190
  TRouteConfig extends AnyRouteConfig = RouteConfig,
176
191
  >(opts: RouterOptions<TRouteConfig>): Router<TRouteConfig> {
192
+ const makeRouteExt = (
193
+ route: AnyRoute,
194
+ router: Router<any, any>,
195
+ ): Pick<AnyRoute, 'useRoute' | 'linkProps' | 'Link' | 'MatchRoute'> => {
196
+ return {
197
+ useRoute: (subRouteId = '.' as any) => {
198
+ const resolvedRouteId = router.resolvePath(
199
+ route.routeId,
200
+ subRouteId as string,
201
+ )
202
+ const resolvedRoute = router.getRoute(resolvedRouteId)
203
+ useRouterSubscription(router)
204
+ invariant(
205
+ resolvedRoute,
206
+ `Could not find a route for route "${
207
+ resolvedRouteId as string
208
+ }"! Did you forget to add it to your route config?`,
209
+ )
210
+ return resolvedRoute
211
+ },
212
+ linkProps: (options) => {
213
+ const {
214
+ // custom props
215
+ type,
216
+ children,
217
+ target,
218
+ activeProps = () => ({ className: 'active' }),
219
+ inactiveProps = () => ({}),
220
+ activeOptions,
221
+ disabled,
222
+ // fromCurrent,
223
+ hash,
224
+ search,
225
+ params,
226
+ to,
227
+ preload,
228
+ preloadDelay,
229
+ preloadMaxAge,
230
+ replace,
231
+ // element props
232
+ style,
233
+ className,
234
+ onClick,
235
+ onFocus,
236
+ onMouseEnter,
237
+ onMouseLeave,
238
+ onTouchStart,
239
+ onTouchEnd,
240
+ ...rest
241
+ } = options
242
+
243
+ const linkInfo = route.buildLink(options)
244
+
245
+ if (linkInfo.type === 'external') {
246
+ const { href } = linkInfo
247
+ return { href }
248
+ }
249
+
250
+ const {
251
+ handleClick,
252
+ handleFocus,
253
+ handleEnter,
254
+ handleLeave,
255
+ isActive,
256
+ next,
257
+ } = linkInfo
258
+
259
+ const composeHandlers =
260
+ (handlers: (undefined | ((e: any) => void))[]) =>
261
+ (e: React.SyntheticEvent) => {
262
+ e.persist()
263
+ handlers.forEach((handler) => {
264
+ if (handler) handler(e)
265
+ })
266
+ }
267
+
268
+ // Get the active props
269
+ const resolvedActiveProps: React.HTMLAttributes<HTMLAnchorElement> =
270
+ isActive ? functionalUpdate(activeProps, {}) ?? {} : {}
271
+
272
+ // Get the inactive props
273
+ const resolvedInactiveProps: React.HTMLAttributes<HTMLAnchorElement> =
274
+ isActive ? {} : functionalUpdate(inactiveProps, {}) ?? {}
275
+
276
+ return {
277
+ ...resolvedActiveProps,
278
+ ...resolvedInactiveProps,
279
+ ...rest,
280
+ href: disabled ? undefined : next.href,
281
+ onClick: composeHandlers([handleClick, onClick]),
282
+ onFocus: composeHandlers([handleFocus, onFocus]),
283
+ onMouseEnter: composeHandlers([handleEnter, onMouseEnter]),
284
+ onMouseLeave: composeHandlers([handleLeave, onMouseLeave]),
285
+ target,
286
+ style: {
287
+ ...style,
288
+ ...resolvedActiveProps.style,
289
+ ...resolvedInactiveProps.style,
290
+ },
291
+ className:
292
+ [
293
+ className,
294
+ resolvedActiveProps.className,
295
+ resolvedInactiveProps.className,
296
+ ]
297
+ .filter(Boolean)
298
+ .join(' ') || undefined,
299
+ ...(disabled
300
+ ? {
301
+ role: 'link',
302
+ 'aria-disabled': true,
303
+ }
304
+ : undefined),
305
+ ['data-status']: isActive ? 'active' : undefined,
306
+ }
307
+ },
308
+ Link: React.forwardRef((props: any, ref) => {
309
+ const linkProps = route.linkProps(props)
310
+
311
+ useRouterSubscription(router)
312
+
313
+ return (
314
+ <a
315
+ {...{
316
+ ref: ref as any,
317
+ ...linkProps,
318
+ children:
319
+ typeof props.children === 'function'
320
+ ? props.children({
321
+ isActive: (linkProps as any)['data-status'] === 'active',
322
+ })
323
+ : props.children,
324
+ }}
325
+ />
326
+ )
327
+ }) as any,
328
+ MatchRoute: (opts) => {
329
+ const { pending, caseSensitive, children, ...rest } = opts
330
+
331
+ const params = route.matchRoute(rest as any, {
332
+ pending,
333
+ caseSensitive,
334
+ })
335
+
336
+ if (!params) {
337
+ return null
338
+ }
339
+
340
+ return typeof opts.children === 'function'
341
+ ? opts.children(params as any)
342
+ : (opts.children as any)
343
+ },
344
+ }
345
+ }
346
+
177
347
  const coreRouter = createRouter<TRouteConfig>({
178
348
  ...opts,
179
349
  createRouter: (router) => {
180
- const routerExt: Pick<Router<any, any>, 'useRoute' | 'useMatch'> = {
181
- useRoute: (routeId) => {
182
- const route = router.getRoute(routeId)
350
+ const routerExt: Pick<Router<any, any>, 'useMatch' | 'useState'> = {
351
+ useState: () => {
183
352
  useRouterSubscription(router)
184
- if (!route) {
185
- throw new Error(
186
- `Could not find a route for route "${
187
- routeId as string
188
- }"! Did you forget to add it to your route config?`,
189
- )
190
- }
191
- return route
353
+ return router.state
192
354
  },
193
355
  useMatch: (routeId) => {
194
- if (routeId === rootRouteId) {
195
- throw new Error(
196
- `"${rootRouteId}" cannot be used with useMatch! Did you mean to useRoute("${rootRouteId}")?`,
197
- )
198
- }
356
+ useRouterSubscription(router)
357
+
358
+ invariant(
359
+ routeId !== rootRouteId,
360
+ `"${rootRouteId}" cannot be used with useMatch! Did you mean to useRoute("${rootRouteId}")?`,
361
+ )
362
+
199
363
  const runtimeMatch = useMatch()
200
364
  const match = router.state.matches.find((d) => d.routeId === routeId)
201
365
 
202
- if (!match) {
203
- throw new Error(
204
- `Could not find a match for route "${
205
- routeId as string
206
- }" being rendered in this component!`,
207
- )
208
- }
209
-
210
- if (runtimeMatch.routeId !== match?.routeId) {
211
- throw new Error(
212
- `useMatch('${
213
- match?.routeId as string
214
- }') is being called in a component that is meant to render the '${
215
- runtimeMatch.routeId
216
- }' route. Did you mean to 'useRoute(${
217
- match?.routeId as string
218
- })' instead?`,
219
- )
220
- }
366
+ invariant(
367
+ match,
368
+ `Could not find a match for route "${
369
+ routeId as string
370
+ }" being rendered in this component!`,
371
+ )
221
372
 
222
- useRouterSubscription(router)
373
+ invariant(
374
+ runtimeMatch.routeId == match?.routeId,
375
+ `useMatch('${
376
+ match?.routeId as string
377
+ }') is being called in a component that is meant to render the '${
378
+ runtimeMatch.routeId
379
+ }' route. Did you mean to 'useRoute(${
380
+ match?.routeId as string
381
+ })' instead?`,
382
+ )
223
383
 
224
384
  if (!match) {
225
- throw new Error('Match not found!')
385
+ invariant('Match not found!')
226
386
  }
227
387
 
228
388
  return match
229
389
  },
230
390
  }
231
391
 
232
- Object.assign(router, routerExt)
392
+ const routeExt = makeRouteExt(router.getRoute('/'), router)
393
+
394
+ Object.assign(router, routerExt, routeExt)
233
395
  },
234
396
  createRoute: ({ router, route }) => {
235
- const routeExt: Pick<AnyRoute, 'linkProps' | 'Link' | 'MatchRoute'> = {
236
- linkProps: (options) => {
237
- const {
238
- // custom props
239
- type,
240
- children,
241
- target,
242
- activeProps = () => ({ className: 'active' }),
243
- inactiveProps = () => ({}),
244
- activeOptions,
245
- disabled,
246
- // fromCurrent,
247
- hash,
248
- search,
249
- params,
250
- to,
251
- preload,
252
- preloadDelay,
253
- preloadMaxAge,
254
- replace,
255
- // element props
256
- style,
257
- className,
258
- onClick,
259
- onFocus,
260
- onMouseEnter,
261
- onMouseLeave,
262
- onTouchStart,
263
- onTouchEnd,
264
- ...rest
265
- } = options
266
-
267
- const linkInfo = route.buildLink(options)
268
-
269
- if (linkInfo.type === 'external') {
270
- const { href } = linkInfo
271
- return { href }
272
- }
397
+ const routeExt = makeRouteExt(route, router)
273
398
 
274
- const {
275
- handleClick,
276
- handleFocus,
277
- handleEnter,
278
- handleLeave,
279
- isActive,
280
- next,
281
- } = linkInfo
282
-
283
- const composeHandlers =
284
- (handlers: (undefined | ((e: any) => void))[]) =>
285
- (e: React.SyntheticEvent) => {
286
- e.persist()
287
- handlers.forEach((handler) => {
288
- if (handler) handler(e)
289
- })
290
- }
291
-
292
- // Get the active props
293
- const resolvedActiveProps: React.HTMLAttributes<HTMLAnchorElement> =
294
- isActive ? functionalUpdate(activeProps) ?? {} : {}
295
-
296
- // Get the inactive props
297
- const resolvedInactiveProps: React.HTMLAttributes<HTMLAnchorElement> =
298
- isActive ? {} : functionalUpdate(inactiveProps) ?? {}
299
-
300
- return {
301
- ...resolvedActiveProps,
302
- ...resolvedInactiveProps,
303
- ...rest,
304
- href: disabled ? undefined : next.href,
305
- onClick: composeHandlers([handleClick, onClick]),
306
- onFocus: composeHandlers([handleFocus, onFocus]),
307
- onMouseEnter: composeHandlers([handleEnter, onMouseEnter]),
308
- onMouseLeave: composeHandlers([handleLeave, onMouseLeave]),
309
- target,
310
- style: {
311
- ...style,
312
- ...resolvedActiveProps.style,
313
- ...resolvedInactiveProps.style,
314
- },
315
- className:
316
- [
317
- className,
318
- resolvedActiveProps.className,
319
- resolvedInactiveProps.className,
320
- ]
321
- .filter(Boolean)
322
- .join(' ') || undefined,
323
- ...(disabled
324
- ? {
325
- role: 'link',
326
- 'aria-disabled': true,
327
- }
328
- : undefined),
329
- ['data-status']: isActive ? 'active' : undefined,
330
- }
331
- },
332
- Link: React.forwardRef((props: any, ref) => {
333
- const linkProps = route.linkProps(props)
334
-
335
- useRouterSubscription(router)
336
-
337
- return (
338
- <a
339
- {...{
340
- ref: ref as any,
341
- ...linkProps,
342
- children:
343
- typeof props.children === 'function'
344
- ? props.children({
345
- isActive:
346
- (linkProps as any)['data-status'] === 'active',
347
- })
348
- : props.children,
349
- }}
350
- />
351
- )
352
- }) as any,
353
- MatchRoute: (opts) => {
354
- const { pending, caseSensitive, children, ...rest } = opts
355
-
356
- const params = route.matchRoute(rest as any, {
357
- pending,
358
- caseSensitive,
359
- })
360
-
361
- // useRouterSubscription(router)
362
-
363
- if (!params) {
364
- return null
365
- }
366
-
367
- return typeof opts.children === 'function'
368
- ? opts.children(params as any)
369
- : (opts.children as any)
370
- },
399
+ Object.assign(route, routeExt)
400
+ },
401
+ createElement: async (element) => {
402
+ if (typeof element === 'function') {
403
+ const res = (await element()) as any
404
+
405
+ // Support direct import() calls
406
+ if (typeof res === 'object' && res.default) {
407
+ return React.createElement(res.default)
408
+ } else {
409
+ return res
410
+ }
371
411
  }
372
412
 
373
- Object.assign(route, routeExt)
413
+ return element
374
414
  },
375
415
  })
376
416
 
@@ -392,14 +432,11 @@ export function RouterProvider<
392
432
  >({ children, router, ...rest }: RouterProps<TRouteConfig, TAllRouteInfo>) {
393
433
  router.update(rest)
394
434
 
395
- useSyncExternalStore(
396
- (cb) => router.subscribe(() => cb()),
397
- () => router.state,
398
- )
435
+ useRouterSubscription(router)
399
436
 
400
437
  useLayoutEffect(() => {
401
- router.mount()
402
- }, [])
438
+ return router.mount()
439
+ }, [router])
403
440
 
404
441
  return (
405
442
  <routerContext.Provider value={{ router }}>
@@ -410,7 +447,7 @@ export function RouterProvider<
410
447
  )
411
448
  }
412
449
 
413
- export function useRouter(): Router {
450
+ function useRouter(): Router {
414
451
  const value = React.useContext(routerContext)
415
452
  warning(!value, 'useRouter must be used inside a <Router> component!')
416
453
 
@@ -419,21 +456,21 @@ export function useRouter(): Router {
419
456
  return value.router as Router
420
457
  }
421
458
 
422
- export function useMatches(): RouteMatch[] {
459
+ function useMatches(): RouteMatch[] {
423
460
  return React.useContext(matchesContext)
424
461
  }
425
462
 
426
- export function useParentMatches(): RouteMatch[] {
427
- const router = useRouter()
428
- const match = useMatch()
429
- const matches = router.state.matches
430
- return matches.slice(
431
- 0,
432
- matches.findIndex((d) => d.matchId === match.matchId) - 1,
433
- )
434
- }
435
-
436
- export function useMatch<T>(): RouteMatch {
463
+ // function useParentMatches(): RouteMatch[] {
464
+ // const router = useRouter()
465
+ // const match = useMatch()
466
+ // const matches = router.state.matches
467
+ // return matches.slice(
468
+ // 0,
469
+ // matches.findIndex((d) => d.matchId === match.matchId) - 1,
470
+ // )
471
+ // }
472
+
473
+ function useMatch<T>(): RouteMatch {
437
474
  return useMatches()?.[0] as RouteMatch
438
475
  }
439
476
 
@@ -445,7 +482,7 @@ export function Outlet() {
445
482
 
446
483
  if (!childMatch) return null
447
484
 
448
- const element = (((): React.ReactNode => {
485
+ const element = ((): React.ReactNode => {
449
486
  if (!childMatch) {
450
487
  return null
451
488
  }
@@ -465,7 +502,7 @@ export function Outlet() {
465
502
  throw childMatch.error
466
503
  }
467
504
 
468
- return <DefaultCatchBoundary error={childMatch.error} />
505
+ return <DefaultErrorBoundary error={childMatch.error} />
469
506
  }
470
507
 
471
508
  if (childMatch.status === 'loading' || childMatch.status === 'idle') {
@@ -482,7 +519,7 @@ export function Outlet() {
482
519
  }
483
520
 
484
521
  return (childMatch.__.element as any) ?? router.options.defaultElement
485
- })() as JSX.Element) ?? <Outlet />
522
+ })() as JSX.Element
486
523
 
487
524
  const catchElement =
488
525
  childMatch?.options.catchElement ?? router.options.defaultCatchElement
@@ -516,7 +553,7 @@ class CatchBoundary extends React.Component<{
516
553
  })
517
554
  }
518
555
  render() {
519
- const catchElement = this.props.catchElement ?? DefaultCatchBoundary
556
+ const catchElement = this.props.catchElement ?? DefaultErrorBoundary
520
557
 
521
558
  if (this.state.error) {
522
559
  return typeof catchElement === 'function'
@@ -528,7 +565,7 @@ class CatchBoundary extends React.Component<{
528
565
  }
529
566
  }
530
567
 
531
- export function DefaultCatchBoundary({ error }: { error: any }) {
568
+ export function DefaultErrorBoundary({ error }: { error: any }) {
532
569
  return (
533
570
  <div style={{ padding: '.5rem', maxWidth: '100%' }}>
534
571
  <strong style={{ fontSize: '1.2rem' }}>Something went wrong!</strong>
@@ -566,3 +603,27 @@ export function DefaultCatchBoundary({ error }: { error: any }) {
566
603
  </div>
567
604
  )
568
605
  }
606
+
607
+ export function usePrompt(message: string, when: boolean | any): void {
608
+ const router = useRouter()
609
+
610
+ React.useEffect(() => {
611
+ if (!when) return
612
+
613
+ let unblock = router.history.block((transition) => {
614
+ if (window.confirm(message)) {
615
+ unblock()
616
+ transition.retry()
617
+ } else {
618
+ router.location.pathname = window.location.pathname
619
+ }
620
+ })
621
+
622
+ return unblock
623
+ }, [when, location, message])
624
+ }
625
+
626
+ export function Prompt({ message, when, children }: PromptProps) {
627
+ usePrompt(message, when ?? true)
628
+ return (children ?? null) as React.ReactNode
629
+ }