@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.
- package/dist/cjs/index.d.cts +1 -0
- package/dist/cjs/manifest.d.cts +23 -0
- package/dist/cjs/router.cjs +3 -1
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +3 -0
- package/dist/cjs/useBlocker.cjs +38 -8
- package/dist/cjs/useBlocker.cjs.map +1 -1
- package/dist/cjs/useBlocker.d.cts +18 -4
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/manifest.d.ts +23 -0
- package/dist/esm/router.d.ts +3 -0
- package/dist/esm/router.js +3 -1
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/useBlocker.d.ts +18 -4
- package/dist/esm/useBlocker.js +38 -8
- package/dist/esm/useBlocker.js.map +1 -1
- package/package.json +1 -1
- package/src/index.tsx +1 -0
- package/src/manifest.ts +31 -0
- package/src/router.ts +6 -0
- package/src/useBlocker.tsx +79 -11
package/dist/esm/useBlocker.js
CHANGED
|
@@ -1,16 +1,46 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { useRouter } from "./useRouter.js";
|
|
3
|
-
function useBlocker(
|
|
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.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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({
|
|
12
|
-
useBlocker(
|
|
13
|
-
return children
|
|
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
|
|
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
package/src/index.tsx
CHANGED
package/src/manifest.ts
ADDED
|
@@ -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) => {
|
package/src/useBlocker.tsx
CHANGED
|
@@ -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
|
-
|
|
8
|
-
condition
|
|
9
|
-
):
|
|
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.
|
|
13
|
-
|
|
14
|
-
|
|
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({
|
|
19
|
-
useBlocker(
|
|
20
|
-
return children
|
|
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
|
-
|
|
92
|
+
blockerFn?: BlockerFn
|
|
25
93
|
condition?: boolean | any
|
|
26
|
-
children?: ReactNode
|
|
94
|
+
children?: ReactNode | (({ proceed, reset }: BlockerResolver) => ReactNode)
|
|
27
95
|
}
|