@tanstack/react-router 1.89.2 → 1.90.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/router.cjs +2 -2
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +2 -2
- package/dist/cjs/useBlocker.cjs +111 -23
- package/dist/cjs/useBlocker.cjs.map +1 -1
- package/dist/cjs/useBlocker.d.cts +58 -13
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/router.d.ts +2 -2
- package/dist/esm/router.js +2 -2
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/useBlocker.d.ts +58 -13
- package/dist/esm/useBlocker.js +111 -23
- package/dist/esm/useBlocker.js.map +1 -1
- package/package.json +2 -2
- package/src/index.tsx +1 -0
- package/src/router.ts +2 -1
- package/src/useBlocker.tsx +245 -41
package/dist/esm/useBlocker.d.ts
CHANGED
|
@@ -1,23 +1,68 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { HistoryAction } from '@tanstack/history';
|
|
2
|
+
import { AnyRoute } from './route.js';
|
|
3
|
+
import { ParseRoute } from './routeInfo.js';
|
|
4
|
+
import { AnyRouter, RegisteredRouter } from './router.js';
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
interface ShouldBlockFnLocation<out TRouteId, out TFullPath, out TAllParams, out TFullSearchSchema> {
|
|
7
|
+
routeId: TRouteId;
|
|
8
|
+
fullPath: TFullPath;
|
|
9
|
+
pathname: string;
|
|
10
|
+
params: TAllParams;
|
|
11
|
+
search: TFullSearchSchema;
|
|
12
|
+
}
|
|
13
|
+
type MakeShouldBlockFnLocationUnion<TRouter extends AnyRouter = RegisteredRouter, TRoute extends AnyRoute = ParseRoute<TRouter['routeTree']>> = TRoute extends any ? ShouldBlockFnLocation<TRoute['id'], TRoute['fullPath'], TRoute['types']['allParams'], TRoute['types']['fullSearchSchema']> : never;
|
|
14
|
+
type BlockerResolver<TRouter extends AnyRouter = RegisteredRouter> = {
|
|
15
|
+
status: 'blocked';
|
|
16
|
+
current: MakeShouldBlockFnLocationUnion<TRouter>;
|
|
17
|
+
next: MakeShouldBlockFnLocationUnion<TRouter>;
|
|
18
|
+
action: HistoryAction;
|
|
5
19
|
proceed: () => void;
|
|
6
20
|
reset: () => void;
|
|
21
|
+
} | {
|
|
22
|
+
status: 'idle';
|
|
23
|
+
current: undefined;
|
|
24
|
+
next: undefined;
|
|
25
|
+
action: undefined;
|
|
26
|
+
proceed: undefined;
|
|
27
|
+
reset: undefined;
|
|
7
28
|
};
|
|
8
|
-
type
|
|
9
|
-
|
|
29
|
+
type ShouldBlockFnArgs<TRouter extends AnyRouter = RegisteredRouter> = {
|
|
30
|
+
current: MakeShouldBlockFnLocationUnion<TRouter>;
|
|
31
|
+
next: MakeShouldBlockFnLocationUnion<TRouter>;
|
|
32
|
+
action: HistoryAction;
|
|
33
|
+
};
|
|
34
|
+
export type ShouldBlockFn<TRouter extends AnyRouter = RegisteredRouter> = (args: ShouldBlockFnArgs<TRouter>) => boolean | Promise<boolean>;
|
|
35
|
+
export type UseBlockerOpts<TRouter extends AnyRouter = RegisteredRouter, TWithResolver extends boolean = boolean> = {
|
|
36
|
+
shouldBlockFn: ShouldBlockFn<TRouter>;
|
|
37
|
+
enableBeforeUnload?: boolean | (() => boolean);
|
|
38
|
+
disabled?: boolean;
|
|
39
|
+
withResolver?: TWithResolver;
|
|
40
|
+
};
|
|
41
|
+
type LegacyBlockerFn = () => Promise<any> | any;
|
|
42
|
+
type LegacyBlockerOpts = {
|
|
43
|
+
blockerFn?: LegacyBlockerFn;
|
|
10
44
|
condition?: boolean | any;
|
|
11
45
|
};
|
|
12
|
-
export declare function useBlocker(
|
|
46
|
+
export declare function useBlocker<TRouter extends AnyRouter = RegisteredRouter, TWithResolver extends boolean = false>(opts: UseBlockerOpts<TRouter, TWithResolver>): TWithResolver extends true ? BlockerResolver<TRouter> : void;
|
|
47
|
+
/**
|
|
48
|
+
* @deprecated Use the shouldBlockFn property instead
|
|
49
|
+
*/
|
|
50
|
+
export declare function useBlocker(blockerFnOrOpts?: LegacyBlockerOpts): BlockerResolver;
|
|
13
51
|
/**
|
|
14
|
-
* @deprecated Use the
|
|
52
|
+
* @deprecated Use the UseBlockerOpts object syntax instead
|
|
15
53
|
*/
|
|
16
|
-
export declare function useBlocker(blockerFn?:
|
|
17
|
-
export declare function Block
|
|
18
|
-
|
|
19
|
-
|
|
54
|
+
export declare function useBlocker(blockerFn?: LegacyBlockerFn, condition?: boolean | any): BlockerResolver;
|
|
55
|
+
export declare function Block<TRouter extends AnyRouter = RegisteredRouter, TWithResolver extends boolean = boolean>(opts: PromptProps<TRouter, TWithResolver>): React.ReactNode;
|
|
56
|
+
/**
|
|
57
|
+
* @deprecated Use the UseBlockerOpts property instead
|
|
58
|
+
*/
|
|
59
|
+
export declare function Block(opts: LegacyPromptProps): React.ReactNode;
|
|
60
|
+
type LegacyPromptProps = {
|
|
61
|
+
blockerFn?: LegacyBlockerFn;
|
|
20
62
|
condition?: boolean | any;
|
|
21
|
-
children?: ReactNode | ((
|
|
63
|
+
children?: React.ReactNode | ((params: BlockerResolver) => React.ReactNode);
|
|
64
|
+
};
|
|
65
|
+
type PromptProps<TRouter extends AnyRouter = RegisteredRouter, TWithResolver extends boolean = boolean, TParams = TWithResolver extends true ? BlockerResolver<TRouter> : void> = UseBlockerOpts<TRouter, TWithResolver> & {
|
|
66
|
+
children?: React.ReactNode | ((params: TParams) => React.ReactNode);
|
|
22
67
|
};
|
|
23
68
|
export {};
|
package/dist/esm/useBlocker.js
CHANGED
|
@@ -1,46 +1,134 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { useRouter } from "./useRouter.js";
|
|
3
|
-
function
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
function _resolveBlockerOpts(opts, condition) {
|
|
4
|
+
if (opts === void 0) {
|
|
5
|
+
return {
|
|
6
|
+
shouldBlockFn: () => true,
|
|
7
|
+
withResolver: false
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
if ("shouldBlockFn" in opts) {
|
|
11
|
+
return opts;
|
|
12
|
+
}
|
|
13
|
+
if (typeof opts === "function") {
|
|
14
|
+
const shouldBlock2 = Boolean(condition ?? true);
|
|
15
|
+
const _customBlockerFn2 = async () => {
|
|
16
|
+
if (shouldBlock2) return await opts();
|
|
17
|
+
return false;
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
shouldBlockFn: _customBlockerFn2,
|
|
21
|
+
enableBeforeUnload: shouldBlock2,
|
|
22
|
+
withResolver: false
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const shouldBlock = Boolean(opts.condition ?? true);
|
|
26
|
+
const fn = opts.blockerFn;
|
|
27
|
+
const _customBlockerFn = async () => {
|
|
28
|
+
if (shouldBlock && fn !== void 0) {
|
|
29
|
+
return await fn();
|
|
30
|
+
}
|
|
31
|
+
return shouldBlock;
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
shouldBlockFn: _customBlockerFn,
|
|
35
|
+
enableBeforeUnload: shouldBlock,
|
|
36
|
+
withResolver: fn === void 0
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function useBlocker(opts, condition) {
|
|
40
|
+
const {
|
|
41
|
+
shouldBlockFn,
|
|
42
|
+
enableBeforeUnload = true,
|
|
43
|
+
disabled = false,
|
|
44
|
+
withResolver = false
|
|
45
|
+
} = _resolveBlockerOpts(opts, condition);
|
|
46
|
+
const router = useRouter();
|
|
47
|
+
const { history } = router;
|
|
9
48
|
const [resolver, setResolver] = React.useState({
|
|
10
49
|
status: "idle",
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
50
|
+
current: void 0,
|
|
51
|
+
next: void 0,
|
|
52
|
+
action: void 0,
|
|
53
|
+
proceed: void 0,
|
|
54
|
+
reset: void 0
|
|
15
55
|
});
|
|
16
56
|
React.useEffect(() => {
|
|
17
|
-
const blockerFnComposed = async () => {
|
|
18
|
-
|
|
19
|
-
|
|
57
|
+
const blockerFnComposed = async (blockerFnArgs) => {
|
|
58
|
+
function getLocation(location) {
|
|
59
|
+
const parsedLocation = router.parseLocation(void 0, location);
|
|
60
|
+
const matchedRoutes = router.getMatchedRoutes(parsedLocation);
|
|
61
|
+
if (matchedRoutes.foundRoute === void 0) {
|
|
62
|
+
throw new Error(`No route found for location ${location.href}`);
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
routeId: matchedRoutes.foundRoute.id,
|
|
66
|
+
fullPath: matchedRoutes.foundRoute.fullPath,
|
|
67
|
+
pathname: parsedLocation.pathname,
|
|
68
|
+
params: matchedRoutes.routeParams,
|
|
69
|
+
search: parsedLocation.search
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const current = getLocation(blockerFnArgs.currentLocation);
|
|
73
|
+
const next = getLocation(blockerFnArgs.nextLocation);
|
|
74
|
+
const shouldBlock = await shouldBlockFn({
|
|
75
|
+
action: blockerFnArgs.action,
|
|
76
|
+
current,
|
|
77
|
+
next
|
|
78
|
+
});
|
|
79
|
+
if (!withResolver) {
|
|
80
|
+
return shouldBlock;
|
|
81
|
+
}
|
|
82
|
+
if (!shouldBlock) {
|
|
83
|
+
return false;
|
|
20
84
|
}
|
|
21
85
|
const promise = new Promise((resolve) => {
|
|
22
86
|
setResolver({
|
|
23
87
|
status: "blocked",
|
|
24
|
-
|
|
25
|
-
|
|
88
|
+
current,
|
|
89
|
+
next,
|
|
90
|
+
action: blockerFnArgs.action,
|
|
91
|
+
proceed: () => resolve(false),
|
|
92
|
+
reset: () => resolve(true)
|
|
26
93
|
});
|
|
27
94
|
});
|
|
28
95
|
const canNavigateAsync = await promise;
|
|
29
96
|
setResolver({
|
|
30
97
|
status: "idle",
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
98
|
+
current: void 0,
|
|
99
|
+
next: void 0,
|
|
100
|
+
action: void 0,
|
|
101
|
+
proceed: void 0,
|
|
102
|
+
reset: void 0
|
|
35
103
|
});
|
|
36
104
|
return canNavigateAsync;
|
|
37
105
|
};
|
|
38
|
-
return
|
|
39
|
-
}, [
|
|
106
|
+
return disabled ? void 0 : history.block({ blockerFn: blockerFnComposed, enableBeforeUnload });
|
|
107
|
+
}, [shouldBlockFn, enableBeforeUnload, disabled, withResolver, history]);
|
|
40
108
|
return resolver;
|
|
41
109
|
}
|
|
42
|
-
|
|
43
|
-
|
|
110
|
+
const _resolvePromptBlockerArgs = (props) => {
|
|
111
|
+
if ("shouldBlockFn" in props) {
|
|
112
|
+
return { ...props };
|
|
113
|
+
}
|
|
114
|
+
const shouldBlock = Boolean(props.condition ?? true);
|
|
115
|
+
const fn = props.blockerFn;
|
|
116
|
+
const _customBlockerFn = async () => {
|
|
117
|
+
if (shouldBlock && fn !== void 0) {
|
|
118
|
+
return await fn();
|
|
119
|
+
}
|
|
120
|
+
return shouldBlock;
|
|
121
|
+
};
|
|
122
|
+
return {
|
|
123
|
+
shouldBlockFn: _customBlockerFn,
|
|
124
|
+
enableBeforeUnload: shouldBlock,
|
|
125
|
+
withResolver: fn === void 0
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
function Block(opts) {
|
|
129
|
+
const { children, ...rest } = opts;
|
|
130
|
+
const args = _resolvePromptBlockerArgs(rest);
|
|
131
|
+
const resolver = useBlocker(args);
|
|
44
132
|
return children ? typeof children === "function" ? children(resolver) : children : null;
|
|
45
133
|
}
|
|
46
134
|
export {
|
|
@@ -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\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 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 const promise = new Promise<boolean>((resolve) => {\n setResolver({\n status: 'blocked',\n proceed: () => resolve(true),\n reset: () => resolve(false),\n })\n })\n\n const canNavigateAsync = await promise\n\n setResolver({\n status: 'idle',\n proceed: () => {},\n reset: () => {},\n })\n\n return canNavigateAsync\n }\n\n return !blockerCondition ? undefined : history.block(blockerFnComposed)\n }, [blockerFn, blockerCondition, history])\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,KAAK;AAC1D,QAAA,EAAE,QAAQ,IAAI,UAAU;AAE9B,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAA0B;AAAA,IAC9D,QAAQ;AAAA,IACR,SAAS,MAAM;AAAA,IAAC;AAAA,IAChB,OAAO,MAAM;AAAA,IAAA;AAAA,EAAC,CACf;AAED,QAAM,UAAU,MAAM;AACpB,UAAM,oBAAoB,YAAY;AAEpC,UAAI,WAAW;AACb,eAAO,MAAM,UAAU;AAAA,MAAA;AAGzB,YAAM,UAAU,IAAI,QAAiB,CAAC,YAAY;AACpC,oBAAA;AAAA,UACV,QAAQ;AAAA,UACR,SAAS,MAAM,QAAQ,IAAI;AAAA,UAC3B,OAAO,MAAM,QAAQ,KAAK;AAAA,QAAA,CAC3B;AAAA,MAAA,CACF;AAED,YAAM,mBAAmB,MAAM;AAEnB,kBAAA;AAAA,QACV,QAAQ;AAAA,QACR,SAAS,MAAM;AAAA,QAAC;AAAA,QAChB,OAAO,MAAM;AAAA,QAAA;AAAA,MAAC,CACf;AAEM,aAAA;AAAA,IACT;AAEA,WAAO,CAAC,mBAAmB,SAAY,QAAQ,MAAM,iBAAiB;AAAA,EACrE,GAAA,CAAC,WAAW,kBAAkB,OAAO,CAAC;AAElC,SAAA;AACT;AAEO,SAAS,MAAM,EAAE,WAAW,WAAW,YAAyB;AACrE,QAAM,WAAW,WAAW,EAAE,WAAW,WAAW;AACpD,SAAO,WACH,OAAO,aAAa,aAClB,SAAS,QAAQ,IACjB,WACF;AACN;"}
|
|
1
|
+
{"version":3,"file":"useBlocker.js","sources":["../../src/useBlocker.tsx"],"sourcesContent":["import * as React from 'react'\nimport { useRouter } from './useRouter'\nimport type {\n BlockerFnArgs,\n HistoryAction,\n HistoryLocation,\n} from '@tanstack/history'\nimport type { AnyRoute } from './route'\nimport type { ParseRoute } from './routeInfo'\nimport type { AnyRouter, RegisteredRouter } from './router'\n\ninterface ShouldBlockFnLocation<\n out TRouteId,\n out TFullPath,\n out TAllParams,\n out TFullSearchSchema,\n> {\n routeId: TRouteId\n fullPath: TFullPath\n pathname: string\n params: TAllParams\n search: TFullSearchSchema\n}\n\ntype AnyShouldBlockFnLocation = ShouldBlockFnLocation<any, any, any, any>\ntype MakeShouldBlockFnLocationUnion<\n TRouter extends AnyRouter = RegisteredRouter,\n TRoute extends AnyRoute = ParseRoute<TRouter['routeTree']>,\n> = TRoute extends any\n ? ShouldBlockFnLocation<\n TRoute['id'],\n TRoute['fullPath'],\n TRoute['types']['allParams'],\n TRoute['types']['fullSearchSchema']\n >\n : never\n\ntype BlockerResolver<TRouter extends AnyRouter = RegisteredRouter> =\n | {\n status: 'blocked'\n current: MakeShouldBlockFnLocationUnion<TRouter>\n next: MakeShouldBlockFnLocationUnion<TRouter>\n action: HistoryAction\n proceed: () => void\n reset: () => void\n }\n | {\n status: 'idle'\n current: undefined\n next: undefined\n action: undefined\n proceed: undefined\n reset: undefined\n }\n\ntype ShouldBlockFnArgs<TRouter extends AnyRouter = RegisteredRouter> = {\n current: MakeShouldBlockFnLocationUnion<TRouter>\n next: MakeShouldBlockFnLocationUnion<TRouter>\n action: HistoryAction\n}\n\nexport type ShouldBlockFn<TRouter extends AnyRouter = RegisteredRouter> = (\n args: ShouldBlockFnArgs<TRouter>,\n) => boolean | Promise<boolean>\nexport type UseBlockerOpts<\n TRouter extends AnyRouter = RegisteredRouter,\n TWithResolver extends boolean = boolean,\n> = {\n shouldBlockFn: ShouldBlockFn<TRouter>\n enableBeforeUnload?: boolean | (() => boolean)\n disabled?: boolean\n withResolver?: TWithResolver\n}\n\ntype LegacyBlockerFn = () => Promise<any> | any\ntype LegacyBlockerOpts = {\n blockerFn?: LegacyBlockerFn\n condition?: boolean | any\n}\n\nfunction _resolveBlockerOpts(\n opts?: UseBlockerOpts | LegacyBlockerOpts | LegacyBlockerFn,\n condition?: boolean | any,\n): UseBlockerOpts {\n if (opts === undefined) {\n return {\n shouldBlockFn: () => true,\n withResolver: false,\n }\n }\n\n if ('shouldBlockFn' in opts) {\n return opts\n }\n\n if (typeof opts === 'function') {\n const shouldBlock = Boolean(condition ?? true)\n\n const _customBlockerFn = async () => {\n if (shouldBlock) return await opts()\n return false\n }\n\n return {\n shouldBlockFn: _customBlockerFn,\n enableBeforeUnload: shouldBlock,\n withResolver: false,\n }\n }\n\n const shouldBlock = Boolean(opts.condition ?? true)\n const fn = opts.blockerFn\n\n const _customBlockerFn = async () => {\n if (shouldBlock && fn !== undefined) {\n return await fn()\n }\n return shouldBlock\n }\n\n return {\n shouldBlockFn: _customBlockerFn,\n enableBeforeUnload: shouldBlock,\n withResolver: fn === undefined,\n }\n}\n\nexport function useBlocker<\n TRouter extends AnyRouter = RegisteredRouter,\n TWithResolver extends boolean = false,\n>(\n opts: UseBlockerOpts<TRouter, TWithResolver>,\n): TWithResolver extends true ? BlockerResolver<TRouter> : void\n\n/**\n * @deprecated Use the shouldBlockFn property instead\n */\nexport function useBlocker(blockerFnOrOpts?: LegacyBlockerOpts): BlockerResolver\n\n/**\n * @deprecated Use the UseBlockerOpts object syntax instead\n */\nexport function useBlocker(\n blockerFn?: LegacyBlockerFn,\n condition?: boolean | any,\n): BlockerResolver\n\nexport function useBlocker(\n opts?: UseBlockerOpts | LegacyBlockerOpts | LegacyBlockerFn,\n condition?: boolean | any,\n): BlockerResolver | void {\n const {\n shouldBlockFn,\n enableBeforeUnload = true,\n disabled = false,\n withResolver = false,\n } = _resolveBlockerOpts(opts, condition)\n\n const router = useRouter()\n const { history } = router\n\n const [resolver, setResolver] = React.useState<BlockerResolver>({\n status: 'idle',\n current: undefined,\n next: undefined,\n action: undefined,\n proceed: undefined,\n reset: undefined,\n })\n\n React.useEffect(() => {\n const blockerFnComposed = async (blockerFnArgs: BlockerFnArgs) => {\n function getLocation(\n location: HistoryLocation,\n ): AnyShouldBlockFnLocation {\n const parsedLocation = router.parseLocation(undefined, location)\n const matchedRoutes = router.getMatchedRoutes(parsedLocation)\n if (matchedRoutes.foundRoute === undefined) {\n throw new Error(`No route found for location ${location.href}`)\n }\n return {\n routeId: matchedRoutes.foundRoute.id,\n fullPath: matchedRoutes.foundRoute.fullPath,\n pathname: parsedLocation.pathname,\n params: matchedRoutes.routeParams,\n search: parsedLocation.search,\n }\n }\n\n const current = getLocation(blockerFnArgs.currentLocation)\n const next = getLocation(blockerFnArgs.nextLocation)\n\n const shouldBlock = await shouldBlockFn({\n action: blockerFnArgs.action,\n current,\n next,\n })\n if (!withResolver) {\n return shouldBlock\n }\n\n if (!shouldBlock) {\n return false\n }\n\n const promise = new Promise<boolean>((resolve) => {\n setResolver({\n status: 'blocked',\n current,\n next,\n action: blockerFnArgs.action,\n proceed: () => resolve(false),\n reset: () => resolve(true),\n })\n })\n\n const canNavigateAsync = await promise\n setResolver({\n status: 'idle',\n current: undefined,\n next: undefined,\n action: undefined,\n proceed: undefined,\n reset: undefined,\n })\n\n return canNavigateAsync\n }\n\n return disabled\n ? undefined\n : history.block({ blockerFn: blockerFnComposed, enableBeforeUnload })\n }, [shouldBlockFn, enableBeforeUnload, disabled, withResolver, history])\n\n return resolver\n}\n\nconst _resolvePromptBlockerArgs = (\n props: PromptProps | LegacyPromptProps,\n): UseBlockerOpts => {\n if ('shouldBlockFn' in props) {\n return { ...props }\n }\n\n const shouldBlock = Boolean(props.condition ?? true)\n const fn = props.blockerFn\n\n const _customBlockerFn = async () => {\n if (shouldBlock && fn !== undefined) {\n return await fn()\n }\n return shouldBlock\n }\n\n return {\n shouldBlockFn: _customBlockerFn,\n enableBeforeUnload: shouldBlock,\n withResolver: fn === undefined,\n }\n}\n\nexport function Block<\n TRouter extends AnyRouter = RegisteredRouter,\n TWithResolver extends boolean = boolean,\n>(opts: PromptProps<TRouter, TWithResolver>): React.ReactNode\n\n/**\n * @deprecated Use the UseBlockerOpts property instead\n */\nexport function Block(opts: LegacyPromptProps): React.ReactNode\n\nexport function Block(opts: PromptProps | LegacyPromptProps): React.ReactNode {\n const { children, ...rest } = opts\n const args = _resolvePromptBlockerArgs(rest)\n\n const resolver = useBlocker(args)\n return children\n ? typeof children === 'function'\n ? children(resolver as any)\n : children\n : null\n}\n\ntype LegacyPromptProps = {\n blockerFn?: LegacyBlockerFn\n condition?: boolean | any\n children?: React.ReactNode | ((params: BlockerResolver) => React.ReactNode)\n}\n\ntype PromptProps<\n TRouter extends AnyRouter = RegisteredRouter,\n TWithResolver extends boolean = boolean,\n TParams = TWithResolver extends true ? BlockerResolver<TRouter> : void,\n> = UseBlockerOpts<TRouter, TWithResolver> & {\n children?: React.ReactNode | ((params: TParams) => React.ReactNode)\n}\n"],"names":["shouldBlock","_customBlockerFn"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/react-router",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.90.0",
|
|
4
4
|
"description": "Modern and scalable routing for React applications",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"jsesc": "^3.0.2",
|
|
54
54
|
"tiny-invariant": "^1.3.3",
|
|
55
55
|
"tiny-warning": "^1.0.3",
|
|
56
|
-
"@tanstack/history": "1.
|
|
56
|
+
"@tanstack/history": "1.90.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@testing-library/jest-dom": "^6.6.3",
|
package/src/index.tsx
CHANGED
|
@@ -301,6 +301,7 @@ export type {
|
|
|
301
301
|
DefaultTransformerStringify,
|
|
302
302
|
} from './transformer'
|
|
303
303
|
|
|
304
|
+
export type { UseBlockerOpts, ShouldBlockFn } from './useBlocker'
|
|
304
305
|
export { useBlocker, Block } from './useBlocker'
|
|
305
306
|
|
|
306
307
|
export { useNavigate, Navigate } from './useNavigate'
|
package/src/router.ts
CHANGED
|
@@ -1058,6 +1058,7 @@ export class Router<
|
|
|
1058
1058
|
|
|
1059
1059
|
parseLocation = (
|
|
1060
1060
|
previousLocation?: ParsedLocation<FullSearchSchema<TRouteTree>>,
|
|
1061
|
+
locationToParse?: HistoryLocation,
|
|
1061
1062
|
): ParsedLocation<FullSearchSchema<TRouteTree>> => {
|
|
1062
1063
|
const parse = ({
|
|
1063
1064
|
pathname,
|
|
@@ -1078,7 +1079,7 @@ export class Router<
|
|
|
1078
1079
|
}
|
|
1079
1080
|
}
|
|
1080
1081
|
|
|
1081
|
-
const location = parse(this.history.location)
|
|
1082
|
+
const location = parse(locationToParse ?? this.history.location)
|
|
1082
1083
|
|
|
1083
1084
|
const { __tempLocation, __tempKey } = location.state
|
|
1084
1085
|
|
package/src/useBlocker.tsx
CHANGED
|
@@ -1,92 +1,296 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
import { useRouter } from './useRouter'
|
|
3
|
-
import type {
|
|
4
|
-
|
|
3
|
+
import type {
|
|
4
|
+
BlockerFnArgs,
|
|
5
|
+
HistoryAction,
|
|
6
|
+
HistoryLocation,
|
|
7
|
+
} from '@tanstack/history'
|
|
8
|
+
import type { AnyRoute } from './route'
|
|
9
|
+
import type { ParseRoute } from './routeInfo'
|
|
10
|
+
import type { AnyRouter, RegisteredRouter } from './router'
|
|
5
11
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
12
|
+
interface ShouldBlockFnLocation<
|
|
13
|
+
out TRouteId,
|
|
14
|
+
out TFullPath,
|
|
15
|
+
out TAllParams,
|
|
16
|
+
out TFullSearchSchema,
|
|
17
|
+
> {
|
|
18
|
+
routeId: TRouteId
|
|
19
|
+
fullPath: TFullPath
|
|
20
|
+
pathname: string
|
|
21
|
+
params: TAllParams
|
|
22
|
+
search: TFullSearchSchema
|
|
10
23
|
}
|
|
11
24
|
|
|
12
|
-
type
|
|
13
|
-
|
|
25
|
+
type AnyShouldBlockFnLocation = ShouldBlockFnLocation<any, any, any, any>
|
|
26
|
+
type MakeShouldBlockFnLocationUnion<
|
|
27
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
28
|
+
TRoute extends AnyRoute = ParseRoute<TRouter['routeTree']>,
|
|
29
|
+
> = TRoute extends any
|
|
30
|
+
? ShouldBlockFnLocation<
|
|
31
|
+
TRoute['id'],
|
|
32
|
+
TRoute['fullPath'],
|
|
33
|
+
TRoute['types']['allParams'],
|
|
34
|
+
TRoute['types']['fullSearchSchema']
|
|
35
|
+
>
|
|
36
|
+
: never
|
|
37
|
+
|
|
38
|
+
type BlockerResolver<TRouter extends AnyRouter = RegisteredRouter> =
|
|
39
|
+
| {
|
|
40
|
+
status: 'blocked'
|
|
41
|
+
current: MakeShouldBlockFnLocationUnion<TRouter>
|
|
42
|
+
next: MakeShouldBlockFnLocationUnion<TRouter>
|
|
43
|
+
action: HistoryAction
|
|
44
|
+
proceed: () => void
|
|
45
|
+
reset: () => void
|
|
46
|
+
}
|
|
47
|
+
| {
|
|
48
|
+
status: 'idle'
|
|
49
|
+
current: undefined
|
|
50
|
+
next: undefined
|
|
51
|
+
action: undefined
|
|
52
|
+
proceed: undefined
|
|
53
|
+
reset: undefined
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
type ShouldBlockFnArgs<TRouter extends AnyRouter = RegisteredRouter> = {
|
|
57
|
+
current: MakeShouldBlockFnLocationUnion<TRouter>
|
|
58
|
+
next: MakeShouldBlockFnLocationUnion<TRouter>
|
|
59
|
+
action: HistoryAction
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type ShouldBlockFn<TRouter extends AnyRouter = RegisteredRouter> = (
|
|
63
|
+
args: ShouldBlockFnArgs<TRouter>,
|
|
64
|
+
) => boolean | Promise<boolean>
|
|
65
|
+
export type UseBlockerOpts<
|
|
66
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
67
|
+
TWithResolver extends boolean = boolean,
|
|
68
|
+
> = {
|
|
69
|
+
shouldBlockFn: ShouldBlockFn<TRouter>
|
|
70
|
+
enableBeforeUnload?: boolean | (() => boolean)
|
|
71
|
+
disabled?: boolean
|
|
72
|
+
withResolver?: TWithResolver
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
type LegacyBlockerFn = () => Promise<any> | any
|
|
76
|
+
type LegacyBlockerOpts = {
|
|
77
|
+
blockerFn?: LegacyBlockerFn
|
|
14
78
|
condition?: boolean | any
|
|
15
79
|
}
|
|
16
80
|
|
|
17
|
-
|
|
81
|
+
function _resolveBlockerOpts(
|
|
82
|
+
opts?: UseBlockerOpts | LegacyBlockerOpts | LegacyBlockerFn,
|
|
83
|
+
condition?: boolean | any,
|
|
84
|
+
): UseBlockerOpts {
|
|
85
|
+
if (opts === undefined) {
|
|
86
|
+
return {
|
|
87
|
+
shouldBlockFn: () => true,
|
|
88
|
+
withResolver: false,
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if ('shouldBlockFn' in opts) {
|
|
93
|
+
return opts
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (typeof opts === 'function') {
|
|
97
|
+
const shouldBlock = Boolean(condition ?? true)
|
|
98
|
+
|
|
99
|
+
const _customBlockerFn = async () => {
|
|
100
|
+
if (shouldBlock) return await opts()
|
|
101
|
+
return false
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
shouldBlockFn: _customBlockerFn,
|
|
106
|
+
enableBeforeUnload: shouldBlock,
|
|
107
|
+
withResolver: false,
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const shouldBlock = Boolean(opts.condition ?? true)
|
|
112
|
+
const fn = opts.blockerFn
|
|
113
|
+
|
|
114
|
+
const _customBlockerFn = async () => {
|
|
115
|
+
if (shouldBlock && fn !== undefined) {
|
|
116
|
+
return await fn()
|
|
117
|
+
}
|
|
118
|
+
return shouldBlock
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
shouldBlockFn: _customBlockerFn,
|
|
123
|
+
enableBeforeUnload: shouldBlock,
|
|
124
|
+
withResolver: fn === undefined,
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function useBlocker<
|
|
129
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
130
|
+
TWithResolver extends boolean = false,
|
|
131
|
+
>(
|
|
132
|
+
opts: UseBlockerOpts<TRouter, TWithResolver>,
|
|
133
|
+
): TWithResolver extends true ? BlockerResolver<TRouter> : void
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @deprecated Use the shouldBlockFn property instead
|
|
137
|
+
*/
|
|
138
|
+
export function useBlocker(blockerFnOrOpts?: LegacyBlockerOpts): BlockerResolver
|
|
18
139
|
|
|
19
140
|
/**
|
|
20
|
-
* @deprecated Use the
|
|
141
|
+
* @deprecated Use the UseBlockerOpts object syntax instead
|
|
21
142
|
*/
|
|
22
143
|
export function useBlocker(
|
|
23
|
-
blockerFn?:
|
|
144
|
+
blockerFn?: LegacyBlockerFn,
|
|
24
145
|
condition?: boolean | any,
|
|
25
146
|
): BlockerResolver
|
|
26
147
|
|
|
27
148
|
export function useBlocker(
|
|
28
|
-
|
|
149
|
+
opts?: UseBlockerOpts | LegacyBlockerOpts | LegacyBlockerFn,
|
|
29
150
|
condition?: boolean | any,
|
|
30
|
-
): BlockerResolver {
|
|
31
|
-
const {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const { history } =
|
|
151
|
+
): BlockerResolver | void {
|
|
152
|
+
const {
|
|
153
|
+
shouldBlockFn,
|
|
154
|
+
enableBeforeUnload = true,
|
|
155
|
+
disabled = false,
|
|
156
|
+
withResolver = false,
|
|
157
|
+
} = _resolveBlockerOpts(opts, condition)
|
|
158
|
+
|
|
159
|
+
const router = useRouter()
|
|
160
|
+
const { history } = router
|
|
40
161
|
|
|
41
162
|
const [resolver, setResolver] = React.useState<BlockerResolver>({
|
|
42
163
|
status: 'idle',
|
|
43
|
-
|
|
44
|
-
|
|
164
|
+
current: undefined,
|
|
165
|
+
next: undefined,
|
|
166
|
+
action: undefined,
|
|
167
|
+
proceed: undefined,
|
|
168
|
+
reset: undefined,
|
|
45
169
|
})
|
|
46
170
|
|
|
47
171
|
React.useEffect(() => {
|
|
48
|
-
const blockerFnComposed = async () => {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
172
|
+
const blockerFnComposed = async (blockerFnArgs: BlockerFnArgs) => {
|
|
173
|
+
function getLocation(
|
|
174
|
+
location: HistoryLocation,
|
|
175
|
+
): AnyShouldBlockFnLocation {
|
|
176
|
+
const parsedLocation = router.parseLocation(undefined, location)
|
|
177
|
+
const matchedRoutes = router.getMatchedRoutes(parsedLocation)
|
|
178
|
+
if (matchedRoutes.foundRoute === undefined) {
|
|
179
|
+
throw new Error(`No route found for location ${location.href}`)
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
routeId: matchedRoutes.foundRoute.id,
|
|
183
|
+
fullPath: matchedRoutes.foundRoute.fullPath,
|
|
184
|
+
pathname: parsedLocation.pathname,
|
|
185
|
+
params: matchedRoutes.routeParams,
|
|
186
|
+
search: parsedLocation.search,
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const current = getLocation(blockerFnArgs.currentLocation)
|
|
191
|
+
const next = getLocation(blockerFnArgs.nextLocation)
|
|
192
|
+
|
|
193
|
+
const shouldBlock = await shouldBlockFn({
|
|
194
|
+
action: blockerFnArgs.action,
|
|
195
|
+
current,
|
|
196
|
+
next,
|
|
197
|
+
})
|
|
198
|
+
if (!withResolver) {
|
|
199
|
+
return shouldBlock
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (!shouldBlock) {
|
|
203
|
+
return false
|
|
52
204
|
}
|
|
53
205
|
|
|
54
206
|
const promise = new Promise<boolean>((resolve) => {
|
|
55
207
|
setResolver({
|
|
56
208
|
status: 'blocked',
|
|
57
|
-
|
|
58
|
-
|
|
209
|
+
current,
|
|
210
|
+
next,
|
|
211
|
+
action: blockerFnArgs.action,
|
|
212
|
+
proceed: () => resolve(false),
|
|
213
|
+
reset: () => resolve(true),
|
|
59
214
|
})
|
|
60
215
|
})
|
|
61
216
|
|
|
62
217
|
const canNavigateAsync = await promise
|
|
63
|
-
|
|
64
218
|
setResolver({
|
|
65
219
|
status: 'idle',
|
|
66
|
-
|
|
67
|
-
|
|
220
|
+
current: undefined,
|
|
221
|
+
next: undefined,
|
|
222
|
+
action: undefined,
|
|
223
|
+
proceed: undefined,
|
|
224
|
+
reset: undefined,
|
|
68
225
|
})
|
|
69
226
|
|
|
70
227
|
return canNavigateAsync
|
|
71
228
|
}
|
|
72
229
|
|
|
73
|
-
return
|
|
74
|
-
|
|
230
|
+
return disabled
|
|
231
|
+
? undefined
|
|
232
|
+
: history.block({ blockerFn: blockerFnComposed, enableBeforeUnload })
|
|
233
|
+
}, [shouldBlockFn, enableBeforeUnload, disabled, withResolver, history])
|
|
75
234
|
|
|
76
235
|
return resolver
|
|
77
236
|
}
|
|
78
237
|
|
|
79
|
-
|
|
80
|
-
|
|
238
|
+
const _resolvePromptBlockerArgs = (
|
|
239
|
+
props: PromptProps | LegacyPromptProps,
|
|
240
|
+
): UseBlockerOpts => {
|
|
241
|
+
if ('shouldBlockFn' in props) {
|
|
242
|
+
return { ...props }
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const shouldBlock = Boolean(props.condition ?? true)
|
|
246
|
+
const fn = props.blockerFn
|
|
247
|
+
|
|
248
|
+
const _customBlockerFn = async () => {
|
|
249
|
+
if (shouldBlock && fn !== undefined) {
|
|
250
|
+
return await fn()
|
|
251
|
+
}
|
|
252
|
+
return shouldBlock
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
shouldBlockFn: _customBlockerFn,
|
|
257
|
+
enableBeforeUnload: shouldBlock,
|
|
258
|
+
withResolver: fn === undefined,
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export function Block<
|
|
263
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
264
|
+
TWithResolver extends boolean = boolean,
|
|
265
|
+
>(opts: PromptProps<TRouter, TWithResolver>): React.ReactNode
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* @deprecated Use the UseBlockerOpts property instead
|
|
269
|
+
*/
|
|
270
|
+
export function Block(opts: LegacyPromptProps): React.ReactNode
|
|
271
|
+
|
|
272
|
+
export function Block(opts: PromptProps | LegacyPromptProps): React.ReactNode {
|
|
273
|
+
const { children, ...rest } = opts
|
|
274
|
+
const args = _resolvePromptBlockerArgs(rest)
|
|
275
|
+
|
|
276
|
+
const resolver = useBlocker(args)
|
|
81
277
|
return children
|
|
82
278
|
? typeof children === 'function'
|
|
83
|
-
? children(resolver)
|
|
279
|
+
? children(resolver as any)
|
|
84
280
|
: children
|
|
85
281
|
: null
|
|
86
282
|
}
|
|
87
283
|
|
|
88
|
-
|
|
89
|
-
blockerFn?:
|
|
284
|
+
type LegacyPromptProps = {
|
|
285
|
+
blockerFn?: LegacyBlockerFn
|
|
90
286
|
condition?: boolean | any
|
|
91
|
-
children?: ReactNode | ((
|
|
287
|
+
children?: React.ReactNode | ((params: BlockerResolver) => React.ReactNode)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
type PromptProps<
|
|
291
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
292
|
+
TWithResolver extends boolean = boolean,
|
|
293
|
+
TParams = TWithResolver extends true ? BlockerResolver<TRouter> : void,
|
|
294
|
+
> = UseBlockerOpts<TRouter, TWithResolver> & {
|
|
295
|
+
children?: React.ReactNode | ((params: TParams) => React.ReactNode)
|
|
92
296
|
}
|