@shuvi/router-react 0.0.1-rc.7 → 1.0.0-rc.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/esm/Link.d.ts +35 -0
- package/esm/Link.js +91 -0
- package/esm/MemoryRouter.d.ts +22 -0
- package/esm/MemoryRouter.js +37 -0
- package/esm/Router.d.ts +19 -0
- package/esm/Router.js +46 -0
- package/esm/RouterView.d.ts +5 -0
- package/esm/RouterView.js +40 -0
- package/esm/constants.d.ts +1 -0
- package/esm/constants.js +1 -0
- package/esm/contexts.d.ts +17 -0
- package/esm/contexts.js +20 -0
- package/esm/hooks.d.ts +57 -0
- package/esm/hooks.js +116 -0
- package/esm/index.d.ts +9 -0
- package/esm/index.js +9 -0
- package/esm/types.d.ts +47 -0
- package/esm/types.js +1 -0
- package/esm/utils.d.ts +9 -0
- package/esm/utils.js +42 -0
- package/lib/Link.d.ts +25 -1
- package/lib/Link.js +45 -28
- package/lib/MemoryRouter.d.ts +3 -3
- package/lib/MemoryRouter.js +20 -22
- package/lib/Router.d.ts +2 -2
- package/lib/Router.js +23 -28
- package/lib/RouterView.d.ts +1 -1
- package/lib/RouterView.js +22 -21
- package/lib/constants.js +1 -0
- package/lib/contexts.d.ts +14 -3
- package/lib/contexts.js +7 -9
- package/lib/hooks.d.ts +18 -2
- package/lib/hooks.js +34 -30
- package/lib/index.d.ts +4 -2
- package/lib/index.js +25 -9
- package/lib/types.d.ts +6 -12
- package/lib/utils.d.ts +0 -2
- package/lib/utils.js +5 -13
- package/package.json +24 -18
package/esm/Link.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { State, PathRecord } from '@shuvi/router';
|
|
3
|
+
/**
|
|
4
|
+
* The public API for rendering a history-aware `<a>`.
|
|
5
|
+
* ```ts
|
|
6
|
+
* // jump to `/about`
|
|
7
|
+
* <Link to="/about">About</Link>
|
|
8
|
+
* // jump with query
|
|
9
|
+
* <Link to="/about?sort=name">About</Link>
|
|
10
|
+
* // with some state
|
|
11
|
+
* <Link to="/about" state={{fromDashboard: true}}>About</Link>
|
|
12
|
+
* // props `to` could be a object
|
|
13
|
+
* <Link to={{
|
|
14
|
+
* pathname: "/about",
|
|
15
|
+
* search: "?sort=name",
|
|
16
|
+
* hash: "#the-hash",
|
|
17
|
+
* }}>About</Link>
|
|
18
|
+
* // props target '_self' | '_blank', default is '_self'
|
|
19
|
+
* <Link to="/about" target="_self">About</Link>
|
|
20
|
+
* // overrides default redirect mode by `replace`
|
|
21
|
+
* <Link to="/about" replace>About</Link>
|
|
22
|
+
* // if `onClick` function, run it first
|
|
23
|
+
* <Link to="/about" onClick={fn}>About</Link>
|
|
24
|
+
* // other props will be delivered to `<a>`
|
|
25
|
+
* <Link to="/about" a='a' b='b'>About</Link> => <{...rest} a>
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare const Link: React.ForwardRefExoticComponent<LinkProps & React.RefAttributes<HTMLAnchorElement>>;
|
|
29
|
+
export interface LinkProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {
|
|
30
|
+
replace?: boolean;
|
|
31
|
+
state?: State;
|
|
32
|
+
to: PathRecord;
|
|
33
|
+
prefetch?: boolean;
|
|
34
|
+
onMouseEnter?: (e: any) => void;
|
|
35
|
+
}
|
package/esm/Link.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import * as React from 'react';
|
|
13
|
+
import * as PropTypes from 'prop-types';
|
|
14
|
+
import { useHref, useNavigate, useResolvedPath } from '.';
|
|
15
|
+
import { pathToString } from '@shuvi/router';
|
|
16
|
+
import { __DEV__ } from './constants';
|
|
17
|
+
import { useCurrentRoute } from './hooks';
|
|
18
|
+
function isModifiedEvent(event) {
|
|
19
|
+
return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The public API for rendering a history-aware `<a>`.
|
|
23
|
+
* ```ts
|
|
24
|
+
* // jump to `/about`
|
|
25
|
+
* <Link to="/about">About</Link>
|
|
26
|
+
* // jump with query
|
|
27
|
+
* <Link to="/about?sort=name">About</Link>
|
|
28
|
+
* // with some state
|
|
29
|
+
* <Link to="/about" state={{fromDashboard: true}}>About</Link>
|
|
30
|
+
* // props `to` could be a object
|
|
31
|
+
* <Link to={{
|
|
32
|
+
* pathname: "/about",
|
|
33
|
+
* search: "?sort=name",
|
|
34
|
+
* hash: "#the-hash",
|
|
35
|
+
* }}>About</Link>
|
|
36
|
+
* // props target '_self' | '_blank', default is '_self'
|
|
37
|
+
* <Link to="/about" target="_self">About</Link>
|
|
38
|
+
* // overrides default redirect mode by `replace`
|
|
39
|
+
* <Link to="/about" replace>About</Link>
|
|
40
|
+
* // if `onClick` function, run it first
|
|
41
|
+
* <Link to="/about" onClick={fn}>About</Link>
|
|
42
|
+
* // other props will be delivered to `<a>`
|
|
43
|
+
* <Link to="/about" a='a' b='b'>About</Link> => <{...rest} a>
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export const Link = React.forwardRef(function LinkWithRef(_a, ref) {
|
|
47
|
+
var { onClick, replace: replaceProp = false, state, target, to } = _a, rest = __rest(_a, ["onClick", "replace", "state", "target", "to"]);
|
|
48
|
+
let href = useHref(to);
|
|
49
|
+
let navigate = useNavigate();
|
|
50
|
+
const location = useCurrentRoute();
|
|
51
|
+
let path = useResolvedPath(to);
|
|
52
|
+
function handleClick(event) {
|
|
53
|
+
if (onClick)
|
|
54
|
+
onClick(event);
|
|
55
|
+
if (!event.defaultPrevented && // onClick prevented default
|
|
56
|
+
event.button === 0 && // Ignore everything but left clicks
|
|
57
|
+
(!target || target === '_self') && // Let browser handle "target=_blank" etc.
|
|
58
|
+
!isModifiedEvent(event) // Ignore clicks with modifier keys
|
|
59
|
+
) {
|
|
60
|
+
event.preventDefault();
|
|
61
|
+
// If the URL hasn't changed, a regular <a> will do a replace instead of
|
|
62
|
+
// a push, so do the same here.
|
|
63
|
+
let replace = !!replaceProp ||
|
|
64
|
+
(location && pathToString(location)) === pathToString(path);
|
|
65
|
+
navigate(to, { replace, state });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return (
|
|
69
|
+
// @ts-ignore
|
|
70
|
+
React.createElement("a", Object.assign({}, rest, { href: href, onClick: handleClick, ref: ref, target: target })));
|
|
71
|
+
});
|
|
72
|
+
if (__DEV__) {
|
|
73
|
+
Link.displayName = 'Link';
|
|
74
|
+
Link.propTypes = {
|
|
75
|
+
onClick: PropTypes.func,
|
|
76
|
+
replace: PropTypes.bool,
|
|
77
|
+
state: PropTypes.object,
|
|
78
|
+
target: PropTypes.string,
|
|
79
|
+
prefetch: PropTypes.bool,
|
|
80
|
+
onMouseEnter: PropTypes.func,
|
|
81
|
+
// @ts-ignore proptypes's bug?
|
|
82
|
+
to: PropTypes.oneOfType([
|
|
83
|
+
PropTypes.string,
|
|
84
|
+
PropTypes.shape({
|
|
85
|
+
pathname: PropTypes.string,
|
|
86
|
+
search: PropTypes.string,
|
|
87
|
+
hash: PropTypes.string
|
|
88
|
+
})
|
|
89
|
+
]).isRequired
|
|
90
|
+
};
|
|
91
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as PropTypes from 'prop-types';
|
|
3
|
+
import { IMemoryRouterProps } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* a <Router> that stores all entries in memory.
|
|
6
|
+
*/
|
|
7
|
+
export declare function MemoryRouter({ basename, children, routes, initialEntries, initialIndex }: IMemoryRouterProps): React.ReactElement;
|
|
8
|
+
export declare namespace MemoryRouter {
|
|
9
|
+
var displayName: string;
|
|
10
|
+
var propTypes: {
|
|
11
|
+
children: PropTypes.Requireable<PropTypes.ReactNodeLike>;
|
|
12
|
+
routes: PropTypes.Requireable<(object | null | undefined)[]>;
|
|
13
|
+
initialEntries: PropTypes.Requireable<(string | PropTypes.InferProps<{
|
|
14
|
+
pathname: PropTypes.Requireable<string>;
|
|
15
|
+
search: PropTypes.Requireable<string>;
|
|
16
|
+
hash: PropTypes.Requireable<string>;
|
|
17
|
+
state: PropTypes.Requireable<object>;
|
|
18
|
+
key: PropTypes.Requireable<string>;
|
|
19
|
+
}> | null | undefined)[]>;
|
|
20
|
+
initialIndex: PropTypes.Requireable<number>;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as PropTypes from 'prop-types';
|
|
3
|
+
import { createMemoryHistory, createRouter } from '@shuvi/router';
|
|
4
|
+
import { Router } from './Router';
|
|
5
|
+
import { __DEV__ } from './constants';
|
|
6
|
+
/**
|
|
7
|
+
* a <Router> that stores all entries in memory.
|
|
8
|
+
*/
|
|
9
|
+
export function MemoryRouter({ basename, children, routes, initialEntries, initialIndex }) {
|
|
10
|
+
let routerRef = React.useRef();
|
|
11
|
+
if (routerRef.current == null) {
|
|
12
|
+
routerRef.current = createRouter({
|
|
13
|
+
basename,
|
|
14
|
+
routes: routes || [],
|
|
15
|
+
history: createMemoryHistory({ initialEntries, initialIndex })
|
|
16
|
+
}).init();
|
|
17
|
+
}
|
|
18
|
+
return React.createElement(Router, { children: children, router: routerRef.current });
|
|
19
|
+
}
|
|
20
|
+
if (__DEV__) {
|
|
21
|
+
MemoryRouter.displayName = 'MemoryRouter';
|
|
22
|
+
MemoryRouter.propTypes = {
|
|
23
|
+
children: PropTypes.node,
|
|
24
|
+
routes: PropTypes.arrayOf(PropTypes.object),
|
|
25
|
+
initialEntries: PropTypes.arrayOf(PropTypes.oneOfType([
|
|
26
|
+
PropTypes.string,
|
|
27
|
+
PropTypes.shape({
|
|
28
|
+
pathname: PropTypes.string,
|
|
29
|
+
search: PropTypes.string,
|
|
30
|
+
hash: PropTypes.string,
|
|
31
|
+
state: PropTypes.object,
|
|
32
|
+
key: PropTypes.string
|
|
33
|
+
})
|
|
34
|
+
])),
|
|
35
|
+
initialIndex: PropTypes.number
|
|
36
|
+
};
|
|
37
|
+
}
|
package/esm/Router.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as PropTypes from 'prop-types';
|
|
3
|
+
import { IRouterProps } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* Provides location context for the rest of the app.
|
|
6
|
+
*
|
|
7
|
+
* Note: You usually won't render a <Router> directly. Instead, you'll render a
|
|
8
|
+
* router that is more specific to your environment such as a <BrowserRouter>
|
|
9
|
+
* in web browsers or a <StaticRouter> for server rendering.
|
|
10
|
+
*/
|
|
11
|
+
export declare function Router({ children, static: staticProp, router }: IRouterProps): React.ReactElement;
|
|
12
|
+
export declare namespace Router {
|
|
13
|
+
var displayName: string;
|
|
14
|
+
var propTypes: {
|
|
15
|
+
children: PropTypes.Requireable<PropTypes.ReactNodeLike>;
|
|
16
|
+
router: PropTypes.Requireable<object>;
|
|
17
|
+
static: PropTypes.Requireable<boolean>;
|
|
18
|
+
};
|
|
19
|
+
}
|
package/esm/Router.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useRef, useReducer } from 'react';
|
|
3
|
+
import * as PropTypes from 'prop-types';
|
|
4
|
+
import invariant from '@shuvi/utils/lib/invariant';
|
|
5
|
+
import { RouterContext, RouteContext } from './contexts';
|
|
6
|
+
import { useInRouterContext } from './hooks';
|
|
7
|
+
import { __DEV__ } from './constants';
|
|
8
|
+
import { useIsomorphicEffect } from './utils';
|
|
9
|
+
/**
|
|
10
|
+
* Provides location context for the rest of the app.
|
|
11
|
+
*
|
|
12
|
+
* Note: You usually won't render a <Router> directly. Instead, you'll render a
|
|
13
|
+
* router that is more specific to your environment such as a <BrowserRouter>
|
|
14
|
+
* in web browsers or a <StaticRouter> for server rendering.
|
|
15
|
+
*/
|
|
16
|
+
export function Router({ children = null, static: staticProp = false, router }) {
|
|
17
|
+
invariant(!useInRouterContext(), `You cannot render a <Router> inside another <Router>.` +
|
|
18
|
+
` You never need more than one.`);
|
|
19
|
+
const contextVal = React.useMemo(() => {
|
|
20
|
+
return {
|
|
21
|
+
static: staticProp,
|
|
22
|
+
router: router
|
|
23
|
+
};
|
|
24
|
+
}, [staticProp, router]);
|
|
25
|
+
const unmount = useRef(false);
|
|
26
|
+
const forceupdate = useReducer(s => s * -1, 1)[1];
|
|
27
|
+
useIsomorphicEffect(() => () => (unmount.current = true), []);
|
|
28
|
+
useIsomorphicEffect(() => {
|
|
29
|
+
router.listen(() => {
|
|
30
|
+
if (unmount.current) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
forceupdate();
|
|
34
|
+
});
|
|
35
|
+
}, [router]);
|
|
36
|
+
return (React.createElement(RouterContext.Provider, { value: contextVal },
|
|
37
|
+
React.createElement(RouteContext.Provider, { children: children, value: router.current })));
|
|
38
|
+
}
|
|
39
|
+
if (__DEV__) {
|
|
40
|
+
Router.displayName = 'Router';
|
|
41
|
+
Router.propTypes = {
|
|
42
|
+
children: PropTypes.node,
|
|
43
|
+
router: PropTypes.object,
|
|
44
|
+
static: PropTypes.bool
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { joinPaths } from '@shuvi/router';
|
|
3
|
+
import { useCurrentRoute } from './hooks';
|
|
4
|
+
import { __DEV__ } from './constants';
|
|
5
|
+
import { MatchedRouteContext } from './contexts';
|
|
6
|
+
import { warningOnce, readOnly } from './utils';
|
|
7
|
+
const defaultElement = React.createElement(RouterView, null);
|
|
8
|
+
function MatchedRoute({ match, depth, parentPathname, parentParams }) {
|
|
9
|
+
const { route, params, pathname } = match;
|
|
10
|
+
const element = React.useMemo(() => route.component
|
|
11
|
+
? React.createElement(route.component, route.props)
|
|
12
|
+
: defaultElement, [route.component, route.props, defaultElement]);
|
|
13
|
+
return (React.createElement(MatchedRouteContext.Provider, { children: element, value: {
|
|
14
|
+
depth: depth + 1,
|
|
15
|
+
params: readOnly(Object.assign(Object.assign({}, parentParams), params)),
|
|
16
|
+
pathname: joinPaths([parentPathname, pathname]),
|
|
17
|
+
route
|
|
18
|
+
} }));
|
|
19
|
+
}
|
|
20
|
+
export function RouterView() {
|
|
21
|
+
let { depth, pathname: parentPathname, params: parentParams } = React.useContext(MatchedRouteContext);
|
|
22
|
+
const { matches } = useCurrentRoute();
|
|
23
|
+
if (!matches.length) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
// Otherwise render an element.
|
|
27
|
+
const matched = matches[depth];
|
|
28
|
+
if (!matched) {
|
|
29
|
+
if (__DEV__) {
|
|
30
|
+
warningOnce(parentPathname, false, `Use <RouterView/> under path "${parentPathname}", but it has no children routes.` +
|
|
31
|
+
`\n\n` +
|
|
32
|
+
`Please remove the <RouterView/>.`);
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return (React.createElement(MatchedRoute, { match: matched, depth: depth, parentPathname: parentPathname, parentParams: parentParams }));
|
|
37
|
+
}
|
|
38
|
+
if (__DEV__) {
|
|
39
|
+
RouterView.displayName = 'RouterView';
|
|
40
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const __DEV__: boolean;
|
package/esm/constants.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const __DEV__ = process.env.NODE_ENV !== 'production';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { IRoute } from '@shuvi/router';
|
|
3
|
+
import { IRouterContextObject, IRouteContextObject } from './types';
|
|
4
|
+
export declare const RouterContext: React.Context<IRouterContextObject>;
|
|
5
|
+
export declare const RouteContext: React.Context<IRoute<{
|
|
6
|
+
caseSensitive?: boolean | undefined;
|
|
7
|
+
children?: any[] | undefined;
|
|
8
|
+
component?: any;
|
|
9
|
+
redirect?: string | undefined;
|
|
10
|
+
props?: import("@shuvi/router").IRouteComponentProps | undefined;
|
|
11
|
+
path: string;
|
|
12
|
+
filepath?: string | undefined;
|
|
13
|
+
id?: string | undefined;
|
|
14
|
+
__componentSourceWithAffix__?: string | undefined;
|
|
15
|
+
__resolveWeak__?: (() => any) | undefined;
|
|
16
|
+
}>>;
|
|
17
|
+
export declare const MatchedRouteContext: React.Context<IRouteContextObject<{}>>;
|
package/esm/contexts.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { readOnly } from './utils';
|
|
3
|
+
import { __DEV__ } from './constants';
|
|
4
|
+
export const RouterContext = React.createContext(null);
|
|
5
|
+
if (__DEV__) {
|
|
6
|
+
RouterContext.displayName = 'Router';
|
|
7
|
+
}
|
|
8
|
+
export const RouteContext = React.createContext(null);
|
|
9
|
+
if (__DEV__) {
|
|
10
|
+
RouterContext.displayName = 'Route';
|
|
11
|
+
}
|
|
12
|
+
export const MatchedRouteContext = React.createContext({
|
|
13
|
+
depth: 0,
|
|
14
|
+
params: readOnly({}),
|
|
15
|
+
pathname: '',
|
|
16
|
+
route: null
|
|
17
|
+
});
|
|
18
|
+
if (__DEV__) {
|
|
19
|
+
MatchedRouteContext.displayName = 'MatchedRoute';
|
|
20
|
+
}
|
package/esm/hooks.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { IParams, IPathMatch, Blocker, Path, PathRecord, IRouter, IPathPattern } from '@shuvi/router';
|
|
2
|
+
import { INavigateFunction, IRouteContextObject } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* is just point `router.current` object
|
|
5
|
+
*/
|
|
6
|
+
export declare function useCurrentRoute(): import("@shuvi/router").IRoute<{
|
|
7
|
+
caseSensitive?: boolean | undefined;
|
|
8
|
+
children?: any[] | undefined;
|
|
9
|
+
component?: any;
|
|
10
|
+
redirect?: string | undefined;
|
|
11
|
+
props?: import("@shuvi/router").IRouteComponentProps | undefined;
|
|
12
|
+
path: string;
|
|
13
|
+
filepath?: string | undefined;
|
|
14
|
+
id?: string | undefined;
|
|
15
|
+
__componentSourceWithAffix__?: string | undefined;
|
|
16
|
+
__resolveWeak__?: (() => any) | undefined;
|
|
17
|
+
}>;
|
|
18
|
+
/**
|
|
19
|
+
* Blocks all navigation attempts. This is useful for preventing the page from
|
|
20
|
+
* changing until some condition is met, like saving form data.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useBlocker(blocker: Blocker, when?: boolean): void;
|
|
23
|
+
/**
|
|
24
|
+
* Returns the full href for the given "to" value. This is useful for building
|
|
25
|
+
* custom links that are also accessible and preserve right-click behavior.
|
|
26
|
+
*/
|
|
27
|
+
export declare function useHref(to: PathRecord): string;
|
|
28
|
+
/**
|
|
29
|
+
* Returns true if this component is a descendant of a <Router>.
|
|
30
|
+
*/
|
|
31
|
+
export declare function useInRouterContext(): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Returns true if the URL for the given "to" value matches the current URL.
|
|
34
|
+
* This is useful for components that need to know "active" state, e.g.
|
|
35
|
+
* <NavLink>.
|
|
36
|
+
*/
|
|
37
|
+
export declare function useMatch(pattern: IPathPattern): IPathMatch | null;
|
|
38
|
+
/**
|
|
39
|
+
* Returns an imperative method for changing the location. Used by <Link>s, but
|
|
40
|
+
* may also be used by other elements to change the location.
|
|
41
|
+
*/
|
|
42
|
+
export declare function useNavigate(): INavigateFunction;
|
|
43
|
+
/**
|
|
44
|
+
* Returns an object of key/value pairs of the dynamic params from the current
|
|
45
|
+
* URL that were matched by the route path.
|
|
46
|
+
*/
|
|
47
|
+
export declare function useParams(): IParams;
|
|
48
|
+
/**
|
|
49
|
+
* Resolves the pathname of the given `to` value against the current location.
|
|
50
|
+
*/
|
|
51
|
+
export declare function useResolvedPath(to: PathRecord): Path;
|
|
52
|
+
/**
|
|
53
|
+
* Returns the current router object
|
|
54
|
+
* two parts, one is router behavior(browser history mode, hash history mode), another is router content
|
|
55
|
+
*/
|
|
56
|
+
export declare function useRouter(): IRouter;
|
|
57
|
+
export declare function useMatchedRoute<ExtendedTypes = {}>(): IRouteContextObject<ExtendedTypes>;
|
package/esm/hooks.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useContext } from 'react';
|
|
3
|
+
import { matchPathname } from '@shuvi/router';
|
|
4
|
+
import invariant from '@shuvi/utils/lib/invariant';
|
|
5
|
+
import { RouterContext, RouteContext, MatchedRouteContext } from './contexts';
|
|
6
|
+
import { warning } from './utils';
|
|
7
|
+
/**
|
|
8
|
+
* is just point `router.current` object
|
|
9
|
+
*/
|
|
10
|
+
export function useCurrentRoute() {
|
|
11
|
+
return useContext(RouteContext);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Blocks all navigation attempts. This is useful for preventing the page from
|
|
15
|
+
* changing until some condition is met, like saving form data.
|
|
16
|
+
*/
|
|
17
|
+
export function useBlocker(blocker, when = true) {
|
|
18
|
+
invariant(useInRouterContext(), `useBlocker() may be used only in the context of a <Router> component.`);
|
|
19
|
+
const { router } = useContext(RouterContext);
|
|
20
|
+
React.useEffect(() => {
|
|
21
|
+
if (!when)
|
|
22
|
+
return;
|
|
23
|
+
let unblock = router.block((tx) => {
|
|
24
|
+
let autoUnblockingTx = Object.assign(Object.assign({}, tx), { retry() {
|
|
25
|
+
// Automatically unblock the transition so it can play all the way
|
|
26
|
+
// through before retrying it. TODO: Figure out how to re-enable
|
|
27
|
+
// this block if the transition is cancelled for some reason.
|
|
28
|
+
unblock();
|
|
29
|
+
tx.retry();
|
|
30
|
+
} });
|
|
31
|
+
blocker(autoUnblockingTx);
|
|
32
|
+
});
|
|
33
|
+
return unblock;
|
|
34
|
+
}, [router, blocker, when]);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Returns the full href for the given "to" value. This is useful for building
|
|
38
|
+
* custom links that are also accessible and preserve right-click behavior.
|
|
39
|
+
*/
|
|
40
|
+
export function useHref(to) {
|
|
41
|
+
invariant(useInRouterContext(), `useHref() may be used only in the context of a <Router> component.`);
|
|
42
|
+
const { router } = useContext(RouterContext);
|
|
43
|
+
const path = useResolvedPath(to);
|
|
44
|
+
return router.resolve(path).href;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Returns true if this component is a descendant of a <Router>.
|
|
48
|
+
*/
|
|
49
|
+
export function useInRouterContext() {
|
|
50
|
+
return useContext(RouterContext) != null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Returns true if the URL for the given "to" value matches the current URL.
|
|
54
|
+
* This is useful for components that need to know "active" state, e.g.
|
|
55
|
+
* <NavLink>.
|
|
56
|
+
*/
|
|
57
|
+
export function useMatch(pattern) {
|
|
58
|
+
invariant(useInRouterContext(), `useMatch() may be used only in the context of a <Router> component.`);
|
|
59
|
+
const { pathname } = useCurrentRoute();
|
|
60
|
+
return matchPathname(pattern, pathname);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Returns an imperative method for changing the location. Used by <Link>s, but
|
|
64
|
+
* may also be used by other elements to change the location.
|
|
65
|
+
*/
|
|
66
|
+
export function useNavigate() {
|
|
67
|
+
invariant(useInRouterContext(), `useNavigate() may be used only in the context of a <Router> component.`);
|
|
68
|
+
const { router } = useContext(RouterContext);
|
|
69
|
+
const { pathname } = useContext(MatchedRouteContext);
|
|
70
|
+
const activeRef = React.useRef(false);
|
|
71
|
+
React.useEffect(() => {
|
|
72
|
+
activeRef.current = true;
|
|
73
|
+
});
|
|
74
|
+
let navigate = React.useCallback((to, options = {}) => {
|
|
75
|
+
if (activeRef.current) {
|
|
76
|
+
if (typeof to === 'number') {
|
|
77
|
+
router.go(to);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
let { path } = router.resolve(to, pathname);
|
|
81
|
+
(!!options.replace ? router.replace : router.push).call(router, path, options.state);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
warning(false, `You should call navigate() in a useEffect, not when ` +
|
|
86
|
+
`your component is first rendered.`);
|
|
87
|
+
}
|
|
88
|
+
}, [router, pathname]);
|
|
89
|
+
return navigate;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Returns an object of key/value pairs of the dynamic params from the current
|
|
93
|
+
* URL that were matched by the route path.
|
|
94
|
+
*/
|
|
95
|
+
export function useParams() {
|
|
96
|
+
return useContext(MatchedRouteContext).params;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Resolves the pathname of the given `to` value against the current location.
|
|
100
|
+
*/
|
|
101
|
+
export function useResolvedPath(to) {
|
|
102
|
+
const { router } = useContext(RouterContext);
|
|
103
|
+
const { pathname } = useContext(MatchedRouteContext);
|
|
104
|
+
return React.useMemo(() => router.resolve(to, pathname).path, [to, pathname]);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Returns the current router object
|
|
108
|
+
* two parts, one is router behavior(browser history mode, hash history mode), another is router content
|
|
109
|
+
*/
|
|
110
|
+
export function useRouter() {
|
|
111
|
+
invariant(useInRouterContext(), `useRouter() may be used only in the context of a <Router> component.`);
|
|
112
|
+
return useContext(RouterContext).router;
|
|
113
|
+
}
|
|
114
|
+
export function useMatchedRoute() {
|
|
115
|
+
return useContext(MatchedRouteContext);
|
|
116
|
+
}
|
package/esm/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { generatePath, useIsomorphicEffect } from './utils';
|
|
2
|
+
export { MemoryRouter } from './MemoryRouter';
|
|
3
|
+
export { RouterView } from './RouterView';
|
|
4
|
+
export { Router } from './Router';
|
|
5
|
+
export { Link, LinkProps } from './Link';
|
|
6
|
+
export * from './hooks';
|
|
7
|
+
export * from './contexts';
|
|
8
|
+
export * from './types';
|
|
9
|
+
export * from '@shuvi/router';
|
package/esm/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { generatePath, useIsomorphicEffect } from './utils';
|
|
2
|
+
export { MemoryRouter } from './MemoryRouter';
|
|
3
|
+
export { RouterView } from './RouterView';
|
|
4
|
+
export { Router } from './Router';
|
|
5
|
+
export { Link } from './Link';
|
|
6
|
+
export * from './hooks';
|
|
7
|
+
export * from './contexts';
|
|
8
|
+
export * from './types';
|
|
9
|
+
export * from '@shuvi/router';
|
package/esm/types.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { IParams, InitialEntry, State, PathRecord, IRouteRecord as IOriginalRouteRecord, IRouter } from '@shuvi/router';
|
|
3
|
+
export declare type IReactRouteRecord<T = {}> = IOriginalRouteRecord<React.ReactNode, T>;
|
|
4
|
+
export declare type IReactPartialRouteRecord = Partial<IReactRouteRecord>;
|
|
5
|
+
export interface IRouterContextObject {
|
|
6
|
+
static: boolean;
|
|
7
|
+
router: IRouter;
|
|
8
|
+
}
|
|
9
|
+
export interface IRouteContextObject<ExtendedTypes = {}> {
|
|
10
|
+
depth: number;
|
|
11
|
+
params: IParams;
|
|
12
|
+
pathname: string;
|
|
13
|
+
route: IReactRouteRecord<ExtendedTypes> | null;
|
|
14
|
+
}
|
|
15
|
+
export interface IMemoryRouterProps {
|
|
16
|
+
basename?: string;
|
|
17
|
+
children?: React.ReactNode;
|
|
18
|
+
routes?: IReactRouteRecord[];
|
|
19
|
+
initialEntries?: InitialEntry[];
|
|
20
|
+
initialIndex?: number;
|
|
21
|
+
}
|
|
22
|
+
export interface INavigateProps {
|
|
23
|
+
to: PathRecord;
|
|
24
|
+
replace?: boolean;
|
|
25
|
+
state?: State;
|
|
26
|
+
}
|
|
27
|
+
export interface IOutletProps {
|
|
28
|
+
}
|
|
29
|
+
export interface IRouterProps {
|
|
30
|
+
children?: React.ReactNode;
|
|
31
|
+
static?: boolean;
|
|
32
|
+
router: IRouter;
|
|
33
|
+
}
|
|
34
|
+
export interface IRoutesProps {
|
|
35
|
+
basename?: string;
|
|
36
|
+
children?: React.ReactNode;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The interface for the navigate() function returned from useNavigate().
|
|
40
|
+
*/
|
|
41
|
+
export interface INavigateFunction {
|
|
42
|
+
(to: PathRecord, options?: {
|
|
43
|
+
replace?: boolean;
|
|
44
|
+
state?: State;
|
|
45
|
+
}): void;
|
|
46
|
+
(delta: number): void;
|
|
47
|
+
}
|
package/esm/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/esm/utils.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { IParams } from '@shuvi/router';
|
|
2
|
+
export declare function useIsomorphicEffect(cb: any, deps: any): void;
|
|
3
|
+
export declare const readOnly: <T extends unknown>(obj: T) => T;
|
|
4
|
+
export declare function warning(cond: boolean, message: string): void;
|
|
5
|
+
export declare function warningOnce(key: string, cond: boolean, message: string): void;
|
|
6
|
+
/**
|
|
7
|
+
* Returns a path with params interpolated.
|
|
8
|
+
*/
|
|
9
|
+
export declare function generatePath(path: string, params?: IParams): string;
|
package/esm/utils.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { matchStringify } from '@shuvi/router';
|
|
2
|
+
import { useLayoutEffect, useEffect } from 'react';
|
|
3
|
+
import { __DEV__ } from './constants';
|
|
4
|
+
export function useIsomorphicEffect(cb, deps) {
|
|
5
|
+
if (typeof window !== 'undefined') {
|
|
6
|
+
useLayoutEffect(cb, deps);
|
|
7
|
+
}
|
|
8
|
+
else {
|
|
9
|
+
useEffect(cb, deps);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export const readOnly = __DEV__
|
|
13
|
+
? obj => Object.freeze(obj)
|
|
14
|
+
: obj => obj;
|
|
15
|
+
export function warning(cond, message) {
|
|
16
|
+
if (!cond) {
|
|
17
|
+
if (typeof console !== 'undefined')
|
|
18
|
+
console.warn(message);
|
|
19
|
+
try {
|
|
20
|
+
// Welcome to debugging React Router!
|
|
21
|
+
//
|
|
22
|
+
// This error is thrown as a convenience so you can more easily
|
|
23
|
+
// find the source for a warning that appears in the console by
|
|
24
|
+
// enabling "pause on exceptions" in your JavaScript debugger.
|
|
25
|
+
throw new Error(message);
|
|
26
|
+
}
|
|
27
|
+
catch (e) { }
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const alreadyWarned = {};
|
|
31
|
+
export function warningOnce(key, cond, message) {
|
|
32
|
+
if (!cond && !alreadyWarned[key]) {
|
|
33
|
+
alreadyWarned[key] = true;
|
|
34
|
+
warning(false, message);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Returns a path with params interpolated.
|
|
39
|
+
*/
|
|
40
|
+
export function generatePath(path, params = {}) {
|
|
41
|
+
return matchStringify(path, params);
|
|
42
|
+
}
|