@esmx/router 3.0.0-rc.103
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/LICENSE +21 -0
- package/README.md +77 -0
- package/README.zh-CN.md +158 -0
- package/dist/error.d.ts +23 -0
- package/dist/error.mjs +64 -0
- package/dist/increment-id.d.ts +7 -0
- package/dist/increment-id.mjs +16 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.mjs +13 -0
- package/dist/location.d.ts +22 -0
- package/dist/location.mjs +64 -0
- package/dist/matcher.d.ts +4 -0
- package/dist/matcher.mjs +46 -0
- package/dist/micro-app.d.ts +18 -0
- package/dist/micro-app.mjs +85 -0
- package/dist/navigation.d.ts +45 -0
- package/dist/navigation.mjs +153 -0
- package/dist/options.d.ts +4 -0
- package/dist/options.mjs +94 -0
- package/dist/route-task.d.ts +40 -0
- package/dist/route-task.mjs +77 -0
- package/dist/route-transition.d.ts +53 -0
- package/dist/route-transition.mjs +356 -0
- package/dist/route.d.ts +77 -0
- package/dist/route.mjs +223 -0
- package/dist/router-link.d.ts +10 -0
- package/dist/router-link.mjs +139 -0
- package/dist/router.d.ts +122 -0
- package/dist/router.mjs +355 -0
- package/dist/scroll.d.ts +33 -0
- package/dist/scroll.mjs +49 -0
- package/dist/types.d.ts +282 -0
- package/dist/types.mjs +18 -0
- package/dist/util.d.ts +27 -0
- package/dist/util.mjs +67 -0
- package/package.json +62 -0
- package/src/error.ts +84 -0
- package/src/increment-id.ts +12 -0
- package/src/index.ts +67 -0
- package/src/location.ts +124 -0
- package/src/matcher.ts +68 -0
- package/src/micro-app.ts +101 -0
- package/src/navigation.ts +202 -0
- package/src/options.ts +135 -0
- package/src/route-task.ts +102 -0
- package/src/route-transition.ts +472 -0
- package/src/route.ts +335 -0
- package/src/router-link.ts +238 -0
- package/src/router.ts +395 -0
- package/src/scroll.ts +106 -0
- package/src/types.ts +381 -0
- package/src/util.ts +133 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
const CSS_CLASSES = {
|
|
2
|
+
BASE: "router-link",
|
|
3
|
+
ACTIVE: "router-link-active",
|
|
4
|
+
EXACT_ACTIVE: "router-link-exact-active"
|
|
5
|
+
};
|
|
6
|
+
function normalizeNavigationType(props) {
|
|
7
|
+
if (props.replace) {
|
|
8
|
+
console.warn(
|
|
9
|
+
'[RouterLink] The `replace` property is deprecated and will be removed in a future version.\nPlease use `type="replace"` instead.\nBefore: <RouterLink replace={true} />\nAfter: <RouterLink type="replace" />'
|
|
10
|
+
);
|
|
11
|
+
return "replace";
|
|
12
|
+
}
|
|
13
|
+
return props.type || "push";
|
|
14
|
+
}
|
|
15
|
+
function getEventTypeList(eventType) {
|
|
16
|
+
const events = Array.isArray(eventType) ? eventType : [eventType];
|
|
17
|
+
const validEvents = events.filter((type) => typeof type === "string").map((type) => type.trim()).filter(Boolean);
|
|
18
|
+
return validEvents.length ? validEvents : ["click"];
|
|
19
|
+
}
|
|
20
|
+
function shouldHandleNavigation(e) {
|
|
21
|
+
var _a;
|
|
22
|
+
if (e.defaultPrevented) return false;
|
|
23
|
+
if (e instanceof MouseEvent) {
|
|
24
|
+
if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return false;
|
|
25
|
+
if (e.button !== void 0 && e.button !== 0) return false;
|
|
26
|
+
}
|
|
27
|
+
(_a = e.preventDefault) == null ? void 0 : _a.call(e);
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
async function executeNavigation(router, props, linkType) {
|
|
31
|
+
const { to, layerOptions } = props;
|
|
32
|
+
switch (linkType) {
|
|
33
|
+
case "push":
|
|
34
|
+
await router.push(to);
|
|
35
|
+
break;
|
|
36
|
+
case "replace":
|
|
37
|
+
await router.replace(to);
|
|
38
|
+
break;
|
|
39
|
+
case "pushWindow":
|
|
40
|
+
await router.pushWindow(to);
|
|
41
|
+
break;
|
|
42
|
+
case "replaceWindow":
|
|
43
|
+
await router.replaceWindow(to);
|
|
44
|
+
break;
|
|
45
|
+
case "pushLayer":
|
|
46
|
+
await router.pushLayer(
|
|
47
|
+
layerOptions ? typeof to === "string" ? { path: to, layer: layerOptions } : { ...to, layer: layerOptions } : to
|
|
48
|
+
);
|
|
49
|
+
break;
|
|
50
|
+
default:
|
|
51
|
+
await router.push(to);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function createNavigateFunction(router, props, navigationType) {
|
|
55
|
+
return async (e) => {
|
|
56
|
+
if (shouldHandleNavigation(e)) {
|
|
57
|
+
await executeNavigation(router, props, navigationType);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function computeAttributes(href, navigationType, isExternal, isActive, isExactActive, activeClass) {
|
|
62
|
+
const isNewWindow = navigationType === "pushWindow";
|
|
63
|
+
const classes = [CSS_CLASSES.BASE];
|
|
64
|
+
if (isActive) {
|
|
65
|
+
classes.push(activeClass || CSS_CLASSES.ACTIVE);
|
|
66
|
+
}
|
|
67
|
+
if (isExactActive) {
|
|
68
|
+
classes.push(CSS_CLASSES.EXACT_ACTIVE);
|
|
69
|
+
}
|
|
70
|
+
const attributes = {
|
|
71
|
+
href,
|
|
72
|
+
class: classes.join(" ")
|
|
73
|
+
};
|
|
74
|
+
if (isNewWindow) {
|
|
75
|
+
attributes.target = "_blank";
|
|
76
|
+
}
|
|
77
|
+
const relParts = [];
|
|
78
|
+
if (isNewWindow) {
|
|
79
|
+
relParts.push("noopener", "noreferrer");
|
|
80
|
+
}
|
|
81
|
+
if (isExternal) {
|
|
82
|
+
relParts.push("external", "nofollow");
|
|
83
|
+
}
|
|
84
|
+
if (relParts.length > 0) {
|
|
85
|
+
attributes.rel = relParts.join(" ");
|
|
86
|
+
}
|
|
87
|
+
return attributes;
|
|
88
|
+
}
|
|
89
|
+
function createEventHandlersGenerator(router, props, navigationType, eventTypes) {
|
|
90
|
+
return (format) => {
|
|
91
|
+
const handlers = {};
|
|
92
|
+
const navigate = createNavigateFunction(router, props, navigationType);
|
|
93
|
+
eventTypes.forEach((eventType) => {
|
|
94
|
+
var _a;
|
|
95
|
+
const eventName = (_a = format == null ? void 0 : format(eventType)) != null ? _a : eventType.toLowerCase();
|
|
96
|
+
handlers[eventName] = (event) => {
|
|
97
|
+
var _a2;
|
|
98
|
+
(_a2 = props.beforeNavigate) == null ? void 0 : _a2.call(props, event, eventType);
|
|
99
|
+
return navigate(event);
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
return handlers;
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
export function createLinkResolver(router, props) {
|
|
106
|
+
const route = router.resolve(props.to);
|
|
107
|
+
const type = normalizeNavigationType(props);
|
|
108
|
+
const href = route.url.href;
|
|
109
|
+
const isActive = router.isRouteMatched(route, props.exact);
|
|
110
|
+
const isExactActive = router.isRouteMatched(route, "exact");
|
|
111
|
+
const isExternal = route.url.origin !== router.route.url.origin;
|
|
112
|
+
const attributes = computeAttributes(
|
|
113
|
+
href,
|
|
114
|
+
type,
|
|
115
|
+
isExternal,
|
|
116
|
+
isActive,
|
|
117
|
+
isExactActive,
|
|
118
|
+
props.activeClass
|
|
119
|
+
);
|
|
120
|
+
const eventTypes = getEventTypeList(props.event || "click");
|
|
121
|
+
const createEventHandlers = createEventHandlersGenerator(
|
|
122
|
+
router,
|
|
123
|
+
props,
|
|
124
|
+
type,
|
|
125
|
+
eventTypes
|
|
126
|
+
);
|
|
127
|
+
const navigate = createNavigateFunction(router, props, type);
|
|
128
|
+
return {
|
|
129
|
+
route,
|
|
130
|
+
type,
|
|
131
|
+
isActive,
|
|
132
|
+
isExactActive,
|
|
133
|
+
isExternal,
|
|
134
|
+
tag: props.tag || "a",
|
|
135
|
+
attributes,
|
|
136
|
+
navigate,
|
|
137
|
+
createEventHandlers
|
|
138
|
+
};
|
|
139
|
+
}
|
package/dist/router.d.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { MicroApp } from './micro-app';
|
|
2
|
+
import { Navigation } from './navigation';
|
|
3
|
+
import { Route } from './route';
|
|
4
|
+
import { RouteTransition } from './route-transition';
|
|
5
|
+
import type { RouteConfirmHook, RouteLayerResult, RouteLocationInput, RouteMatchType, RouteNotifyHook, RouterLinkProps, RouterLinkResolved, RouterOptions, RouterParsedOptions } from './types';
|
|
6
|
+
import { RouterMode, RouteType } from './types';
|
|
7
|
+
export declare class Router {
|
|
8
|
+
readonly options: RouterOptions;
|
|
9
|
+
readonly parsedOptions: RouterParsedOptions;
|
|
10
|
+
readonly isLayer: boolean;
|
|
11
|
+
readonly navigation: Navigation;
|
|
12
|
+
readonly microApp: MicroApp;
|
|
13
|
+
readonly transition: RouteTransition;
|
|
14
|
+
get route(): Route;
|
|
15
|
+
get context(): Record<string | symbol, unknown>;
|
|
16
|
+
get data(): Record<string | symbol, unknown>;
|
|
17
|
+
get root(): string | HTMLElement;
|
|
18
|
+
get mode(): RouterMode;
|
|
19
|
+
get base(): URL;
|
|
20
|
+
get req(): import("http").IncomingMessage | null;
|
|
21
|
+
get res(): import("http").ServerResponse<import("http").IncomingMessage> | null;
|
|
22
|
+
constructor(options: RouterOptions);
|
|
23
|
+
push(toInput: RouteLocationInput): Promise<Route>;
|
|
24
|
+
replace(toInput: RouteLocationInput): Promise<Route>;
|
|
25
|
+
pushWindow(toInput: RouteLocationInput): Promise<Route>;
|
|
26
|
+
replaceWindow(toInput: RouteLocationInput): Promise<Route>;
|
|
27
|
+
restartApp(toInput?: RouteLocationInput): Promise<Route>;
|
|
28
|
+
back(): Promise<Route | null>;
|
|
29
|
+
go(index: number): Promise<Route | null>;
|
|
30
|
+
forward(): Promise<Route | null>;
|
|
31
|
+
/**
|
|
32
|
+
* Parse route location without performing actual navigation
|
|
33
|
+
*
|
|
34
|
+
* This method is used to parse route configuration and return the corresponding route object,
|
|
35
|
+
* but does not trigger actual page navigation. It is mainly used for the following scenarios:
|
|
36
|
+
* - Generate link URLs without jumping
|
|
37
|
+
* - Pre-check route matching
|
|
38
|
+
* - Get route parameters, meta information, etc.
|
|
39
|
+
* - Test the validity of route configuration
|
|
40
|
+
*
|
|
41
|
+
* @param toInput Target route location, can be a string path or route configuration object
|
|
42
|
+
* @returns Parsed route object containing complete route information
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* // Parse string path
|
|
47
|
+
* const route = router.resolve('/user/123');
|
|
48
|
+
* const url = route.url.href; // Get complete URL
|
|
49
|
+
*
|
|
50
|
+
* // Parse named route
|
|
51
|
+
* const userRoute = router.resolve({
|
|
52
|
+
* name: 'user',
|
|
53
|
+
* params: { id: '123' }
|
|
54
|
+
* });
|
|
55
|
+
* console.log(userRoute.params.id); // '123'
|
|
56
|
+
*
|
|
57
|
+
* // Check route validity
|
|
58
|
+
* const testRoute = router.resolve('/some/path');
|
|
59
|
+
* if (testRoute.matched.length > 0) {
|
|
60
|
+
* // Route matched successfully
|
|
61
|
+
* }
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
resolve(toInput: RouteLocationInput, toType?: RouteType): Route;
|
|
65
|
+
/**
|
|
66
|
+
* Check if the route matches the current route
|
|
67
|
+
*
|
|
68
|
+
* @param toRoute Target route object to compare
|
|
69
|
+
* @param matchType Match type
|
|
70
|
+
* - 'route': Route-level matching, compare if route configurations are the same
|
|
71
|
+
* - 'exact': Exact matching, compare if paths are completely the same
|
|
72
|
+
* - 'include': Include matching, check if current path contains target path
|
|
73
|
+
* @returns Whether it matches
|
|
74
|
+
*/
|
|
75
|
+
isRouteMatched(toRoute: Route, matchType?: RouteMatchType): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Resolve router link configuration and return complete link data
|
|
78
|
+
*
|
|
79
|
+
* This method analyzes router link properties and returns a comprehensive
|
|
80
|
+
* link resolution result including route information, navigation functions,
|
|
81
|
+
* HTML attributes, and event handlers. It's primarily used for:
|
|
82
|
+
* - Framework-agnostic link component implementation
|
|
83
|
+
* - Generating link attributes and navigation handlers
|
|
84
|
+
* - Computing active states and CSS classes
|
|
85
|
+
* - Creating event handlers for different frameworks
|
|
86
|
+
*
|
|
87
|
+
* @param props Router link configuration properties
|
|
88
|
+
* @returns Complete link resolution result with all necessary data
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* // Basic link resolution
|
|
93
|
+
* const linkData = router.resolveLink({
|
|
94
|
+
* to: '/user/123',
|
|
95
|
+
* type: 'push'
|
|
96
|
+
* });
|
|
97
|
+
*
|
|
98
|
+
* // Access resolved data
|
|
99
|
+
* console.log(linkData.route.path); // '/user/123'
|
|
100
|
+
* console.log(linkData.attributes.href); // Full href URL
|
|
101
|
+
* console.log(linkData.isActive); // Active state
|
|
102
|
+
*
|
|
103
|
+
* // Use navigation function
|
|
104
|
+
* linkData.navigate(); // Programmatic navigation
|
|
105
|
+
*
|
|
106
|
+
* // Get event handlers for React
|
|
107
|
+
* const handlers = linkData.createEventHandlers(name => `on${name.charAt(0).toUpperCase() + name.slice(1)}`);
|
|
108
|
+
* // handlers.onClick for React
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
resolveLink(props: RouterLinkProps): RouterLinkResolved;
|
|
112
|
+
createLayer(toInput: RouteLocationInput): Promise<{
|
|
113
|
+
promise: Promise<RouteLayerResult>;
|
|
114
|
+
router: Router;
|
|
115
|
+
}>;
|
|
116
|
+
pushLayer(toInput: RouteLocationInput): Promise<RouteLayerResult>;
|
|
117
|
+
closeLayer(data?: any): void;
|
|
118
|
+
renderToString(throwError?: boolean): Promise<string | null>;
|
|
119
|
+
beforeEach(guard: RouteConfirmHook): () => void;
|
|
120
|
+
afterEach(guard: RouteNotifyHook): () => void;
|
|
121
|
+
destroy(): void;
|
|
122
|
+
}
|
package/dist/router.mjs
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
import { LAYER_ID } from "./increment-id.mjs";
|
|
5
|
+
import { MicroApp } from "./micro-app.mjs";
|
|
6
|
+
import { Navigation } from "./navigation.mjs";
|
|
7
|
+
import { parsedOptions } from "./options.mjs";
|
|
8
|
+
import { Route } from "./route.mjs";
|
|
9
|
+
import { RouteTransition } from "./route-transition.mjs";
|
|
10
|
+
import { createLinkResolver } from "./router-link.mjs";
|
|
11
|
+
import { RouterMode, RouteType } from "./types.mjs";
|
|
12
|
+
import { isNotNullish, isPlainObject, isRouteMatched } from "./util.mjs";
|
|
13
|
+
export class Router {
|
|
14
|
+
constructor(options) {
|
|
15
|
+
__publicField(this, "options");
|
|
16
|
+
__publicField(this, "parsedOptions");
|
|
17
|
+
__publicField(this, "isLayer");
|
|
18
|
+
__publicField(this, "navigation");
|
|
19
|
+
__publicField(this, "microApp", new MicroApp());
|
|
20
|
+
// Route transition manager
|
|
21
|
+
__publicField(this, "transition", new RouteTransition(this));
|
|
22
|
+
this.options = options;
|
|
23
|
+
this.parsedOptions = parsedOptions(options);
|
|
24
|
+
this.isLayer = this.parsedOptions.layer;
|
|
25
|
+
this.navigation = new Navigation(
|
|
26
|
+
this.parsedOptions,
|
|
27
|
+
(url, state) => {
|
|
28
|
+
this.transition.to(RouteType.unknown, {
|
|
29
|
+
url,
|
|
30
|
+
state
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
get route() {
|
|
36
|
+
const route = this.transition.route;
|
|
37
|
+
if (route === null) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
"No active route found. Please navigate to a route first using router.push() or router.replace()."
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
return route;
|
|
43
|
+
}
|
|
44
|
+
get context() {
|
|
45
|
+
return this.parsedOptions.context;
|
|
46
|
+
}
|
|
47
|
+
get data() {
|
|
48
|
+
return this.parsedOptions.data;
|
|
49
|
+
}
|
|
50
|
+
get root() {
|
|
51
|
+
return this.parsedOptions.root;
|
|
52
|
+
}
|
|
53
|
+
get mode() {
|
|
54
|
+
return this.parsedOptions.mode;
|
|
55
|
+
}
|
|
56
|
+
get base() {
|
|
57
|
+
return this.parsedOptions.base;
|
|
58
|
+
}
|
|
59
|
+
get req() {
|
|
60
|
+
var _a;
|
|
61
|
+
return (_a = this.parsedOptions.req) != null ? _a : null;
|
|
62
|
+
}
|
|
63
|
+
get res() {
|
|
64
|
+
var _a;
|
|
65
|
+
return (_a = this.parsedOptions.res) != null ? _a : null;
|
|
66
|
+
}
|
|
67
|
+
push(toInput) {
|
|
68
|
+
return this.transition.to(RouteType.push, toInput);
|
|
69
|
+
}
|
|
70
|
+
replace(toInput) {
|
|
71
|
+
return this.transition.to(RouteType.replace, toInput);
|
|
72
|
+
}
|
|
73
|
+
pushWindow(toInput) {
|
|
74
|
+
return this.transition.to(RouteType.pushWindow, toInput);
|
|
75
|
+
}
|
|
76
|
+
replaceWindow(toInput) {
|
|
77
|
+
return this.transition.to(RouteType.replaceWindow, toInput);
|
|
78
|
+
}
|
|
79
|
+
restartApp(toInput) {
|
|
80
|
+
return this.transition.to(
|
|
81
|
+
RouteType.restartApp,
|
|
82
|
+
toInput != null ? toInput : this.route.url.href
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
async back() {
|
|
86
|
+
const result = await this.navigation.go(-1);
|
|
87
|
+
if (result === null) {
|
|
88
|
+
this.parsedOptions.handleBackBoundary(this);
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
return this.transition.to(RouteType.back, {
|
|
92
|
+
url: result.url,
|
|
93
|
+
state: result.state
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
async go(index) {
|
|
97
|
+
if (index === 0) return null;
|
|
98
|
+
const result = await this.navigation.go(index);
|
|
99
|
+
if (result === null) {
|
|
100
|
+
if (index < 0) {
|
|
101
|
+
this.parsedOptions.handleBackBoundary(this);
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
return this.transition.to(RouteType.go, {
|
|
106
|
+
url: result.url,
|
|
107
|
+
state: result.state
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
async forward() {
|
|
111
|
+
const result = await this.navigation.go(1);
|
|
112
|
+
if (result === null) return null;
|
|
113
|
+
return this.transition.to(RouteType.forward, {
|
|
114
|
+
url: result.url,
|
|
115
|
+
state: result.state
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Parse route location without performing actual navigation
|
|
120
|
+
*
|
|
121
|
+
* This method is used to parse route configuration and return the corresponding route object,
|
|
122
|
+
* but does not trigger actual page navigation. It is mainly used for the following scenarios:
|
|
123
|
+
* - Generate link URLs without jumping
|
|
124
|
+
* - Pre-check route matching
|
|
125
|
+
* - Get route parameters, meta information, etc.
|
|
126
|
+
* - Test the validity of route configuration
|
|
127
|
+
*
|
|
128
|
+
* @param toInput Target route location, can be a string path or route configuration object
|
|
129
|
+
* @returns Parsed route object containing complete route information
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```typescript
|
|
133
|
+
* // Parse string path
|
|
134
|
+
* const route = router.resolve('/user/123');
|
|
135
|
+
* const url = route.url.href; // Get complete URL
|
|
136
|
+
*
|
|
137
|
+
* // Parse named route
|
|
138
|
+
* const userRoute = router.resolve({
|
|
139
|
+
* name: 'user',
|
|
140
|
+
* params: { id: '123' }
|
|
141
|
+
* });
|
|
142
|
+
* console.log(userRoute.params.id); // '123'
|
|
143
|
+
*
|
|
144
|
+
* // Check route validity
|
|
145
|
+
* const testRoute = router.resolve('/some/path');
|
|
146
|
+
* if (testRoute.matched.length > 0) {
|
|
147
|
+
* // Route matched successfully
|
|
148
|
+
* }
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
resolve(toInput, toType) {
|
|
152
|
+
var _a, _b;
|
|
153
|
+
return new Route({
|
|
154
|
+
options: this.parsedOptions,
|
|
155
|
+
toType,
|
|
156
|
+
toInput,
|
|
157
|
+
from: (_b = (_a = this.transition.route) == null ? void 0 : _a.url) != null ? _b : null
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Check if the route matches the current route
|
|
162
|
+
*
|
|
163
|
+
* @param toRoute Target route object to compare
|
|
164
|
+
* @param matchType Match type
|
|
165
|
+
* - 'route': Route-level matching, compare if route configurations are the same
|
|
166
|
+
* - 'exact': Exact matching, compare if paths are completely the same
|
|
167
|
+
* - 'include': Include matching, check if current path contains target path
|
|
168
|
+
* @returns Whether it matches
|
|
169
|
+
*/
|
|
170
|
+
isRouteMatched(toRoute, matchType = "include") {
|
|
171
|
+
const currentRoute = this.transition.route;
|
|
172
|
+
if (!currentRoute) return false;
|
|
173
|
+
return isRouteMatched(currentRoute, toRoute, matchType);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Resolve router link configuration and return complete link data
|
|
177
|
+
*
|
|
178
|
+
* This method analyzes router link properties and returns a comprehensive
|
|
179
|
+
* link resolution result including route information, navigation functions,
|
|
180
|
+
* HTML attributes, and event handlers. It's primarily used for:
|
|
181
|
+
* - Framework-agnostic link component implementation
|
|
182
|
+
* - Generating link attributes and navigation handlers
|
|
183
|
+
* - Computing active states and CSS classes
|
|
184
|
+
* - Creating event handlers for different frameworks
|
|
185
|
+
*
|
|
186
|
+
* @param props Router link configuration properties
|
|
187
|
+
* @returns Complete link resolution result with all necessary data
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```typescript
|
|
191
|
+
* // Basic link resolution
|
|
192
|
+
* const linkData = router.resolveLink({
|
|
193
|
+
* to: '/user/123',
|
|
194
|
+
* type: 'push'
|
|
195
|
+
* });
|
|
196
|
+
*
|
|
197
|
+
* // Access resolved data
|
|
198
|
+
* console.log(linkData.route.path); // '/user/123'
|
|
199
|
+
* console.log(linkData.attributes.href); // Full href URL
|
|
200
|
+
* console.log(linkData.isActive); // Active state
|
|
201
|
+
*
|
|
202
|
+
* // Use navigation function
|
|
203
|
+
* linkData.navigate(); // Programmatic navigation
|
|
204
|
+
*
|
|
205
|
+
* // Get event handlers for React
|
|
206
|
+
* const handlers = linkData.createEventHandlers(name => `on${name.charAt(0).toUpperCase() + name.slice(1)}`);
|
|
207
|
+
* // handlers.onClick for React
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
resolveLink(props) {
|
|
211
|
+
return createLinkResolver(this, props);
|
|
212
|
+
}
|
|
213
|
+
async createLayer(toInput) {
|
|
214
|
+
var _a;
|
|
215
|
+
const layerOptions = isPlainObject(toInput) && toInput.layer || {};
|
|
216
|
+
const zIndex = (_a = layerOptions.zIndex) != null ? _a : this.parsedOptions.zIndex + LAYER_ID.next();
|
|
217
|
+
let promiseResolve;
|
|
218
|
+
let promise = new Promise((resolve) => {
|
|
219
|
+
promiseResolve = resolve;
|
|
220
|
+
});
|
|
221
|
+
const router = new Router({
|
|
222
|
+
rootStyle: {
|
|
223
|
+
position: "fixed",
|
|
224
|
+
top: "0",
|
|
225
|
+
left: "0",
|
|
226
|
+
width: "100%",
|
|
227
|
+
height: "100%",
|
|
228
|
+
zIndex: String(zIndex),
|
|
229
|
+
background: "rgba(0,0,0,.6)",
|
|
230
|
+
display: "flex",
|
|
231
|
+
alignItems: "center",
|
|
232
|
+
justifyContent: "center"
|
|
233
|
+
},
|
|
234
|
+
...this.options,
|
|
235
|
+
context: this.parsedOptions.context,
|
|
236
|
+
mode: RouterMode.memory,
|
|
237
|
+
root: void 0,
|
|
238
|
+
...layerOptions.routerOptions,
|
|
239
|
+
handleBackBoundary(router2) {
|
|
240
|
+
router2.destroy();
|
|
241
|
+
promiseResolve({
|
|
242
|
+
type: "close",
|
|
243
|
+
route: router2.route
|
|
244
|
+
});
|
|
245
|
+
},
|
|
246
|
+
handleLayerClose(router2, data) {
|
|
247
|
+
router2.destroy();
|
|
248
|
+
if (isNotNullish(data)) {
|
|
249
|
+
promiseResolve({
|
|
250
|
+
type: "success",
|
|
251
|
+
route: router2.route,
|
|
252
|
+
data
|
|
253
|
+
});
|
|
254
|
+
} else {
|
|
255
|
+
promiseResolve({
|
|
256
|
+
type: "close",
|
|
257
|
+
route: router2.route
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
layer: true
|
|
262
|
+
});
|
|
263
|
+
const initRoute = await router.replace(toInput);
|
|
264
|
+
router.afterEach(async (to, from) => {
|
|
265
|
+
if (![
|
|
266
|
+
RouteType.pushWindow,
|
|
267
|
+
RouteType.replaceWindow,
|
|
268
|
+
RouteType.replace,
|
|
269
|
+
RouteType.restartApp,
|
|
270
|
+
RouteType.pushLayer
|
|
271
|
+
].includes(to.type))
|
|
272
|
+
return;
|
|
273
|
+
let keepAlive = false;
|
|
274
|
+
if (layerOptions.keepAlive === "exact") {
|
|
275
|
+
keepAlive = to.path === initRoute.path;
|
|
276
|
+
} else if (layerOptions.keepAlive === "include") {
|
|
277
|
+
keepAlive = to.path.startsWith(initRoute.path);
|
|
278
|
+
} else if (typeof layerOptions.keepAlive === "function") {
|
|
279
|
+
keepAlive = await layerOptions.keepAlive(to, from, router);
|
|
280
|
+
} else {
|
|
281
|
+
if (layerOptions.shouldClose) {
|
|
282
|
+
console.warn(
|
|
283
|
+
"[esmx-router] RouteLayerOptions.shouldClose is deprecated. Use keepAlive instead. Note: shouldClose returns true to close, keepAlive returns true to keep alive."
|
|
284
|
+
);
|
|
285
|
+
keepAlive = !await layerOptions.shouldClose(
|
|
286
|
+
to,
|
|
287
|
+
from,
|
|
288
|
+
router
|
|
289
|
+
);
|
|
290
|
+
} else {
|
|
291
|
+
keepAlive = to.path === initRoute.path;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (!keepAlive) {
|
|
295
|
+
router.destroy();
|
|
296
|
+
promiseResolve({
|
|
297
|
+
type: "push",
|
|
298
|
+
route: to
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
if (layerOptions.push) {
|
|
303
|
+
router.navigation.pushHistoryState(
|
|
304
|
+
router.route.state,
|
|
305
|
+
router.route.url
|
|
306
|
+
);
|
|
307
|
+
promise = promise.then(async (result) => {
|
|
308
|
+
await this.navigation.backHistoryState();
|
|
309
|
+
return result;
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
if (layerOptions.autoPush) {
|
|
313
|
+
promise = promise.then(async (result) => {
|
|
314
|
+
if (result.type === "push") {
|
|
315
|
+
await this.push(result.route.url.href);
|
|
316
|
+
}
|
|
317
|
+
return result;
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
promise,
|
|
322
|
+
router
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
async pushLayer(toInput) {
|
|
326
|
+
const result = await this.transition.to(RouteType.pushLayer, toInput);
|
|
327
|
+
return result.handleResult;
|
|
328
|
+
}
|
|
329
|
+
closeLayer(data) {
|
|
330
|
+
if (!this.isLayer) return;
|
|
331
|
+
this.parsedOptions.handleLayerClose(this, data);
|
|
332
|
+
}
|
|
333
|
+
async renderToString(throwError = false) {
|
|
334
|
+
var _a, _b;
|
|
335
|
+
try {
|
|
336
|
+
const result = await ((_b = (_a = this.microApp.app) == null ? void 0 : _a.renderToString) == null ? void 0 : _b.call(_a));
|
|
337
|
+
return result != null ? result : null;
|
|
338
|
+
} catch (e) {
|
|
339
|
+
if (throwError) throw e;
|
|
340
|
+
else console.error(e);
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
beforeEach(guard) {
|
|
345
|
+
return this.transition.beforeEach(guard);
|
|
346
|
+
}
|
|
347
|
+
afterEach(guard) {
|
|
348
|
+
return this.transition.afterEach(guard);
|
|
349
|
+
}
|
|
350
|
+
destroy() {
|
|
351
|
+
this.transition.destroy();
|
|
352
|
+
this.navigation.destroy();
|
|
353
|
+
this.microApp.destroy();
|
|
354
|
+
}
|
|
355
|
+
}
|
package/dist/scroll.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** Internal {@link ScrollToOptions | `ScrollToOptions`}: `left` and `top` properties always have values */
|
|
2
|
+
interface _ScrollPosition extends ScrollToOptions {
|
|
3
|
+
left: number;
|
|
4
|
+
top: number;
|
|
5
|
+
}
|
|
6
|
+
export interface ScrollPositionElement extends ScrollToOptions {
|
|
7
|
+
/**
|
|
8
|
+
* A valid CSS selector. Some special characters need to be escaped (https://mathiasbynens.be/notes/css-escapes).
|
|
9
|
+
* @example
|
|
10
|
+
* Here are some examples:
|
|
11
|
+
*
|
|
12
|
+
* - `.title`
|
|
13
|
+
* - `.content:first-child`
|
|
14
|
+
* - `#marker`
|
|
15
|
+
* - `#marker\~with\~symbols`
|
|
16
|
+
* - `#marker.with.dot`: Selects `class="with dot" id="marker"`, not `id="marker.with.dot"`
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
el: string | Element;
|
|
20
|
+
}
|
|
21
|
+
/** Scroll parameters */
|
|
22
|
+
export type ScrollPosition = ScrollToOptions | ScrollPositionElement;
|
|
23
|
+
/** Get current window scroll position */
|
|
24
|
+
export declare const winScrollPos: () => _ScrollPosition;
|
|
25
|
+
/** Scroll to specified position */
|
|
26
|
+
export declare function scrollToPosition(position: ScrollPosition): void;
|
|
27
|
+
/** Stored scroll positions */
|
|
28
|
+
export declare const scrollPositions: Map<string, _ScrollPosition>;
|
|
29
|
+
/** Save scroll position */
|
|
30
|
+
export declare function saveScrollPosition(key: string, scrollPosition?: _ScrollPosition): void;
|
|
31
|
+
/** Get saved scroll position */
|
|
32
|
+
export declare function getSavedScrollPosition(key: string, defaultValue?: _ScrollPosition | null): _ScrollPosition | null;
|
|
33
|
+
export {};
|
package/dist/scroll.mjs
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export const winScrollPos = () => ({
|
|
2
|
+
left: window.scrollX,
|
|
3
|
+
top: window.scrollY
|
|
4
|
+
});
|
|
5
|
+
function getElementPosition(el, offset) {
|
|
6
|
+
const docRect = document.documentElement.getBoundingClientRect();
|
|
7
|
+
const elRect = el.getBoundingClientRect();
|
|
8
|
+
return {
|
|
9
|
+
behavior: offset.behavior,
|
|
10
|
+
left: elRect.left - docRect.left - (offset.left || 0),
|
|
11
|
+
top: elRect.top - docRect.top - (offset.top || 0)
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function scrollToPosition(position) {
|
|
15
|
+
if ("el" in position) {
|
|
16
|
+
const positionEl = position.el;
|
|
17
|
+
const el = typeof positionEl === "string" ? document.querySelector(positionEl) : positionEl;
|
|
18
|
+
if (!el) return;
|
|
19
|
+
position = getElementPosition(el, position);
|
|
20
|
+
}
|
|
21
|
+
if ("scrollBehavior" in document.documentElement.style) {
|
|
22
|
+
window.scrollTo(position);
|
|
23
|
+
} else {
|
|
24
|
+
window.scrollTo(
|
|
25
|
+
Number.isFinite(position.left) ? position.left : window.scrollX,
|
|
26
|
+
Number.isFinite(position.top) ? position.top : window.scrollY
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export const scrollPositions = /* @__PURE__ */ new Map();
|
|
31
|
+
const POSITION_KEY = "__scroll_position_key";
|
|
32
|
+
export function saveScrollPosition(key, scrollPosition = winScrollPos()) {
|
|
33
|
+
scrollPosition = { ...scrollPosition };
|
|
34
|
+
scrollPositions.set(key, scrollPosition);
|
|
35
|
+
try {
|
|
36
|
+
if (location.href !== key) return;
|
|
37
|
+
const stateCopy = {
|
|
38
|
+
...history.state || {},
|
|
39
|
+
[POSITION_KEY]: scrollPosition
|
|
40
|
+
};
|
|
41
|
+
history.replaceState(stateCopy, "");
|
|
42
|
+
} catch (error) {
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function getSavedScrollPosition(key, defaultValue = null) {
|
|
46
|
+
const scroll = scrollPositions.get(key) || history.state[POSITION_KEY];
|
|
47
|
+
scrollPositions.delete(key);
|
|
48
|
+
return scroll || defaultValue;
|
|
49
|
+
}
|