@tanstack/react-router 1.33.7 → 1.34.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.
@@ -1,16 +1,46 @@
1
1
  import * as React from "react";
2
2
  import { useRouter } from "./useRouter.js";
3
- function useBlocker(blockerFn, condition = true) {
3
+ function useBlocker(blockerFnOrOpts, condition) {
4
+ const { blockerFn, blockerCondition } = blockerFnOrOpts ? typeof blockerFnOrOpts === "function" ? { blockerFn: blockerFnOrOpts, blockerCondition: condition ?? true } : {
5
+ blockerFn: blockerFnOrOpts.blockerFn,
6
+ blockerCondition: blockerFnOrOpts.condition ?? true
7
+ } : { blockerFn: void 0, blockerCondition: condition ?? true };
4
8
  const { history } = useRouter();
5
- React.useEffect(() => {
6
- if (!condition)
7
- return;
8
- return history.block(blockerFn);
9
+ const [resolver, setResolver] = React.useState({
10
+ status: "idle",
11
+ proceed: () => {
12
+ },
13
+ reset: () => {
14
+ }
15
+ });
16
+ const createPromise = () => new Promise((resolve) => {
17
+ setResolver({
18
+ status: "idle",
19
+ proceed: () => resolve(true),
20
+ reset: () => resolve(false)
21
+ });
9
22
  });
23
+ const [promise, setPromise] = React.useState(createPromise);
24
+ React.useEffect(() => {
25
+ const blockerFnComposed = async () => {
26
+ if (blockerFn) {
27
+ return await blockerFn();
28
+ }
29
+ setResolver((prev) => ({
30
+ ...prev,
31
+ status: "blocked"
32
+ }));
33
+ const canNavigateAsync = await promise;
34
+ setPromise(createPromise);
35
+ return canNavigateAsync;
36
+ };
37
+ return !blockerCondition ? void 0 : history.block(blockerFnComposed);
38
+ }, [blockerFn, blockerCondition, history, promise]);
39
+ return resolver;
10
40
  }
11
- function Block({ blocker, condition, children }) {
12
- useBlocker(blocker, condition);
13
- return children ?? null;
41
+ function Block({ blockerFn, condition, children }) {
42
+ const resolver = useBlocker({ blockerFn, condition });
43
+ return children ? typeof children === "function" ? children(resolver) : children : null;
14
44
  }
15
45
  export {
16
46
  Block,
@@ -1 +1 @@
1
- {"version":3,"file":"useBlocker.js","sources":["../../src/useBlocker.tsx"],"sourcesContent":["import * as React from 'react'\nimport { useRouter } from './useRouter'\nimport type { BlockerFn } from '@tanstack/history'\nimport type { ReactNode } from './route'\n\nexport function useBlocker(\n blockerFn: BlockerFn,\n condition: boolean | any = true,\n): void {\n const { history } = useRouter()\n\n React.useEffect(() => {\n if (!condition) return\n return history.block(blockerFn)\n })\n}\n\nexport function Block({ blocker, condition, children }: PromptProps) {\n useBlocker(blocker, condition)\n return children ?? null\n}\n\nexport type PromptProps = {\n blocker: BlockerFn\n condition?: boolean | any\n children?: ReactNode\n}\n"],"names":[],"mappings":";;AAKgB,SAAA,WACd,WACA,YAA2B,MACrB;AACA,QAAA,EAAE,YAAY;AAEpB,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC;AAAW;AACT,WAAA,QAAQ,MAAM,SAAS;AAAA,EAAA,CAC/B;AACH;AAEO,SAAS,MAAM,EAAE,SAAS,WAAW,YAAyB;AACnE,aAAW,SAAS,SAAS;AAC7B,SAAO,YAAY;AACrB;"}
1
+ {"version":3,"file":"useBlocker.js","sources":["../../src/useBlocker.tsx"],"sourcesContent":["import * as React from 'react'\nimport { useRouter } from './useRouter'\nimport type { BlockerFn } from '@tanstack/history'\nimport type { ReactNode } from './route'\n\ntype BlockerResolver = {\n status: 'idle' | 'blocked'\n proceed: () => void\n reset: () => void\n}\n\ntype BlockerOpts = {\n blockerFn?: BlockerFn\n condition?: boolean | any\n}\n\nexport function useBlocker(blockerFnOrOpts?: BlockerOpts): BlockerResolver\n\n/**\n * @deprecated Use the BlockerOpts object syntax instead\n */\nexport function useBlocker(\n blockerFn?: BlockerFn,\n condition?: boolean | any,\n): BlockerResolver\n\nexport function useBlocker(\n blockerFnOrOpts?: BlockerFn | BlockerOpts,\n condition?: boolean | any,\n): BlockerResolver {\n const { blockerFn, blockerCondition } = blockerFnOrOpts\n ? typeof blockerFnOrOpts === 'function'\n ? { blockerFn: blockerFnOrOpts, blockerCondition: condition ?? true }\n : {\n blockerFn: blockerFnOrOpts.blockerFn,\n blockerCondition: blockerFnOrOpts.condition ?? true,\n }\n : { blockerFn: undefined, blockerCondition: condition ?? true }\n const { history } = useRouter()\n\n const [resolver, setResolver] = React.useState<BlockerResolver>({\n status: 'idle',\n proceed: () => {},\n reset: () => {},\n })\n\n const createPromise = () =>\n new Promise<boolean>((resolve) => {\n setResolver({\n status: 'idle',\n proceed: () => resolve(true),\n reset: () => resolve(false),\n })\n })\n\n const [promise, setPromise] = React.useState(createPromise)\n\n React.useEffect(() => {\n const blockerFnComposed = async () => {\n // If a function is provided, it takes precedence over the promise blocker\n if (blockerFn) {\n return await blockerFn()\n }\n\n setResolver((prev) => ({\n ...prev,\n status: 'blocked',\n }))\n const canNavigateAsync = await promise\n\n setPromise(createPromise)\n\n return canNavigateAsync\n }\n\n return !blockerCondition ? undefined : history.block(blockerFnComposed)\n }, [blockerFn, blockerCondition, history, promise])\n\n return resolver\n}\n\nexport function Block({ blockerFn, condition, children }: PromptProps) {\n const resolver = useBlocker({ blockerFn, condition })\n return children\n ? typeof children === 'function'\n ? children(resolver)\n : children\n : null\n}\n\nexport type PromptProps = {\n blockerFn?: BlockerFn\n condition?: boolean | any\n children?: ReactNode | (({ proceed, reset }: BlockerResolver) => ReactNode)\n}\n"],"names":[],"mappings":";;AA0BgB,SAAA,WACd,iBACA,WACiB;AACjB,QAAM,EAAE,WAAW,qBAAqB,kBACpC,OAAO,oBAAoB,aACzB,EAAE,WAAW,iBAAiB,kBAAkB,aAAa,SAC7D;AAAA,IACE,WAAW,gBAAgB;AAAA,IAC3B,kBAAkB,gBAAgB,aAAa;AAAA,EAAA,IAEnD,EAAE,WAAW,QAAW,kBAAkB,aAAa;AACrD,QAAA,EAAE,YAAY;AAEpB,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAA0B;AAAA,IAC9D,QAAQ;AAAA,IACR,SAAS,MAAM;AAAA,IAAC;AAAA,IAChB,OAAO,MAAM;AAAA,IAAC;AAAA,EAAA,CACf;AAED,QAAM,gBAAgB,MACpB,IAAI,QAAiB,CAAC,YAAY;AACpB,gBAAA;AAAA,MACV,QAAQ;AAAA,MACR,SAAS,MAAM,QAAQ,IAAI;AAAA,MAC3B,OAAO,MAAM,QAAQ,KAAK;AAAA,IAAA,CAC3B;AAAA,EAAA,CACF;AAEH,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,aAAa;AAE1D,QAAM,UAAU,MAAM;AACpB,UAAM,oBAAoB,YAAY;AAEpC,UAAI,WAAW;AACb,eAAO,MAAM,UAAU;AAAA,MACzB;AAEA,kBAAY,CAAC,UAAU;AAAA,QACrB,GAAG;AAAA,QACH,QAAQ;AAAA,MACR,EAAA;AACF,YAAM,mBAAmB,MAAM;AAE/B,iBAAW,aAAa;AAEjB,aAAA;AAAA,IAAA;AAGT,WAAO,CAAC,mBAAmB,SAAY,QAAQ,MAAM,iBAAiB;AAAA,KACrE,CAAC,WAAW,kBAAkB,SAAS,OAAO,CAAC;AAE3C,SAAA;AACT;AAEO,SAAS,MAAM,EAAE,WAAW,WAAW,YAAyB;AACrE,QAAM,WAAW,WAAW,EAAE,WAAW,UAAW,CAAA;AACpD,SAAO,WACH,OAAO,aAAa,aAClB,SAAS,QAAQ,IACjB,WACF;AACN;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-router",
3
- "version": "1.33.7",
3
+ "version": "1.34.0",
4
4
  "description": "",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
package/src/index.tsx CHANGED
@@ -269,3 +269,4 @@ export {
269
269
  DefaultGlobalNotFound,
270
270
  type NotFoundError,
271
271
  } from './not-found'
272
+ export { type Manifest, type RouterManagedTag } from './manifest'
@@ -0,0 +1,31 @@
1
+ export type Manifest = {
2
+ routes: Record<
3
+ string,
4
+ {
5
+ preloads?: Array<string>
6
+ assets?: Array<RouterManagedTag>
7
+ }
8
+ >
9
+ }
10
+
11
+ export type RouterManagedTag =
12
+ | {
13
+ tag: 'title'
14
+ attrs?: Record<string, any>
15
+ children: string
16
+ }
17
+ | {
18
+ tag: 'meta' | 'link'
19
+ attrs?: Record<string, any>
20
+ children?: never
21
+ }
22
+ | {
23
+ tag: 'script'
24
+ attrs?: Record<string, any>
25
+ children?: string
26
+ }
27
+ | {
28
+ tag: 'style'
29
+ attrs?: Record<string, any>
30
+ children?: string
31
+ }
package/src/router.ts CHANGED
@@ -27,6 +27,7 @@ import {
27
27
  } from './path'
28
28
  import { isRedirect, isResolvedRedirect } from './redirects'
29
29
  import { isNotFound } from './not-found'
30
+ import type { Manifest } from './manifest'
30
31
  import type * as React from 'react'
31
32
  import type {
32
33
  HistoryLocation,
@@ -393,6 +394,7 @@ export type DehydratedRouteMatch = Pick<
393
394
 
394
395
  export interface DehydratedRouter {
395
396
  state: DehydratedRouterState
397
+ manifest?: Manifest
396
398
  }
397
399
 
398
400
  export type RouterConstructorOptions<
@@ -484,6 +486,7 @@ export class Router<
484
486
  injectedHtml: Array<InjectedHtmlEntry> = []
485
487
  dehydratedData?: TDehydrated
486
488
  viewTransitionPromise?: ControlledPromise<true>
489
+ manifest?: Manifest
487
490
 
488
491
  // Must build in constructor
489
492
  __store!: Store<RouterState<TRouteTree>>
@@ -2314,6 +2317,7 @@ export class Router<
2314
2317
  : undefined,
2315
2318
  })),
2316
2319
  },
2320
+ manifest: this.manifest,
2317
2321
  }
2318
2322
  }
2319
2323
 
@@ -2376,6 +2380,8 @@ export class Router<
2376
2380
  matches: matches as any,
2377
2381
  }
2378
2382
  })
2383
+
2384
+ this.manifest = ctx.router.manifest
2379
2385
  }
2380
2386
 
2381
2387
  handleNotFound = (matches: Array<AnyRouteMatch>, err: NotFoundError) => {
@@ -3,25 +3,93 @@ import { useRouter } from './useRouter'
3
3
  import type { BlockerFn } from '@tanstack/history'
4
4
  import type { ReactNode } from './route'
5
5
 
6
+ type BlockerResolver = {
7
+ status: 'idle' | 'blocked'
8
+ proceed: () => void
9
+ reset: () => void
10
+ }
11
+
12
+ type BlockerOpts = {
13
+ blockerFn?: BlockerFn
14
+ condition?: boolean | any
15
+ }
16
+
17
+ export function useBlocker(blockerFnOrOpts?: BlockerOpts): BlockerResolver
18
+
19
+ /**
20
+ * @deprecated Use the BlockerOpts object syntax instead
21
+ */
22
+ export function useBlocker(
23
+ blockerFn?: BlockerFn,
24
+ condition?: boolean | any,
25
+ ): BlockerResolver
26
+
6
27
  export function useBlocker(
7
- blockerFn: BlockerFn,
8
- condition: boolean | any = true,
9
- ): void {
28
+ blockerFnOrOpts?: BlockerFn | BlockerOpts,
29
+ condition?: boolean | any,
30
+ ): BlockerResolver {
31
+ const { blockerFn, blockerCondition } = blockerFnOrOpts
32
+ ? typeof blockerFnOrOpts === 'function'
33
+ ? { blockerFn: blockerFnOrOpts, blockerCondition: condition ?? true }
34
+ : {
35
+ blockerFn: blockerFnOrOpts.blockerFn,
36
+ blockerCondition: blockerFnOrOpts.condition ?? true,
37
+ }
38
+ : { blockerFn: undefined, blockerCondition: condition ?? true }
10
39
  const { history } = useRouter()
11
40
 
12
- React.useEffect(() => {
13
- if (!condition) return
14
- return history.block(blockerFn)
41
+ const [resolver, setResolver] = React.useState<BlockerResolver>({
42
+ status: 'idle',
43
+ proceed: () => {},
44
+ reset: () => {},
15
45
  })
46
+
47
+ const createPromise = () =>
48
+ new Promise<boolean>((resolve) => {
49
+ setResolver({
50
+ status: 'idle',
51
+ proceed: () => resolve(true),
52
+ reset: () => resolve(false),
53
+ })
54
+ })
55
+
56
+ const [promise, setPromise] = React.useState(createPromise)
57
+
58
+ React.useEffect(() => {
59
+ const blockerFnComposed = async () => {
60
+ // If a function is provided, it takes precedence over the promise blocker
61
+ if (blockerFn) {
62
+ return await blockerFn()
63
+ }
64
+
65
+ setResolver((prev) => ({
66
+ ...prev,
67
+ status: 'blocked',
68
+ }))
69
+ const canNavigateAsync = await promise
70
+
71
+ setPromise(createPromise)
72
+
73
+ return canNavigateAsync
74
+ }
75
+
76
+ return !blockerCondition ? undefined : history.block(blockerFnComposed)
77
+ }, [blockerFn, blockerCondition, history, promise])
78
+
79
+ return resolver
16
80
  }
17
81
 
18
- export function Block({ blocker, condition, children }: PromptProps) {
19
- useBlocker(blocker, condition)
20
- return children ?? null
82
+ export function Block({ blockerFn, condition, children }: PromptProps) {
83
+ const resolver = useBlocker({ blockerFn, condition })
84
+ return children
85
+ ? typeof children === 'function'
86
+ ? children(resolver)
87
+ : children
88
+ : null
21
89
  }
22
90
 
23
91
  export type PromptProps = {
24
- blocker: BlockerFn
92
+ blockerFn?: BlockerFn
25
93
  condition?: boolean | any
26
- children?: ReactNode
94
+ children?: ReactNode | (({ proceed, reset }: BlockerResolver) => ReactNode)
27
95
  }