@wcstack/router 1.7.1 → 1.8.1
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/README.ja.md +24 -1
- package/README.md +24 -1
- package/dist/index.d.ts +113 -1
- package/dist/index.esm.js +79 -2
- package/dist/index.esm.js.map +1 -1
- package/dist/index.esm.min.js +1 -1
- package/dist/index.esm.min.js.map +1 -1
- package/package.json +1 -1
package/README.ja.md
CHANGED
|
@@ -141,7 +141,30 @@
|
|
|
141
141
|
| `guardHandler` | ガード判定関数を設定 |
|
|
142
142
|
|
|
143
143
|
ガード判定関数の型:
|
|
144
|
-
|
|
144
|
+
`(toPath: string, fromPath: string) => boolean | Promise<boolean>`
|
|
145
|
+
|
|
146
|
+
#### GuardHandler(wcs-guard-handler)
|
|
147
|
+
|
|
148
|
+
`<wcs-route>` の子要素として配置し、ガード判定関数を宣言的に定義します。`<script type="module">` の `default export` で判定関数を返してください。`<wcs-guard-handler>` 要素自体はパース後にDOMから除去されます。
|
|
149
|
+
|
|
150
|
+
```html
|
|
151
|
+
<wcs-route path="/dashboard" guard="/login">
|
|
152
|
+
<wcs-guard-handler>
|
|
153
|
+
<script type="module">
|
|
154
|
+
export default function(toPath, fromPath) {
|
|
155
|
+
return document.cookie.includes('session=');
|
|
156
|
+
}
|
|
157
|
+
</script>
|
|
158
|
+
</wcs-guard-handler>
|
|
159
|
+
<dashboard-page></dashboard-page>
|
|
160
|
+
</wcs-route>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
- `guard` 属性の値はガードがキャンセルされた場合のリダイレクト先パス
|
|
164
|
+
- 判定関数が `false` を返すとナビゲーションがキャンセルされ、`guard` 属性のパスへ遷移
|
|
165
|
+
- 判定関数は `Promise<boolean>` を返すことも可能(非同期チェック対応)
|
|
166
|
+
- `<wcs-route>` の外に配置された `<wcs-guard-handler>` は無視される
|
|
167
|
+
- `<script type="module">` がない場合、`guardHandler` は設定されない
|
|
145
168
|
|
|
146
169
|
#### 型付きパラメータ
|
|
147
170
|
|
package/README.md
CHANGED
|
@@ -141,7 +141,30 @@ Displays children when the route path matches. Match priority is static paths ov
|
|
|
141
141
|
| `guardHandler` | Sets the guard decision function. |
|
|
142
142
|
|
|
143
143
|
Guard decision function type:
|
|
144
|
-
|
|
144
|
+
`(toPath: string, fromPath: string) => boolean | Promise<boolean>`
|
|
145
|
+
|
|
146
|
+
#### GuardHandler (wcs-guard-handler)
|
|
147
|
+
|
|
148
|
+
Place as a child of `<wcs-route>` to declaratively define a guard decision function. Export the function as the `default export` from a `<script type="module">`. The `<wcs-guard-handler>` element itself is removed from the DOM after parsing.
|
|
149
|
+
|
|
150
|
+
```html
|
|
151
|
+
<wcs-route path="/dashboard" guard="/login">
|
|
152
|
+
<wcs-guard-handler>
|
|
153
|
+
<script type="module">
|
|
154
|
+
export default function(toPath, fromPath) {
|
|
155
|
+
return document.cookie.includes('session=');
|
|
156
|
+
}
|
|
157
|
+
</script>
|
|
158
|
+
</wcs-guard-handler>
|
|
159
|
+
<dashboard-page></dashboard-page>
|
|
160
|
+
</wcs-route>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
- The `guard` attribute value is the redirect path when the guard cancels navigation
|
|
164
|
+
- If the function returns `false`, navigation is cancelled and the user is redirected to the `guard` path
|
|
165
|
+
- The function can return `Promise<boolean>` for async checks
|
|
166
|
+
- `<wcs-guard-handler>` placed outside a `<wcs-route>` is ignored
|
|
167
|
+
- If no `<script type="module">` is present, `guardHandler` is not set
|
|
145
168
|
|
|
146
169
|
#### Typed Parameters
|
|
147
170
|
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ interface ITagNames {
|
|
|
6
6
|
readonly layoutOutlet: string;
|
|
7
7
|
readonly link: string;
|
|
8
8
|
readonly head: string;
|
|
9
|
+
readonly guardHandler: string;
|
|
9
10
|
}
|
|
10
11
|
interface IWritableTagNames {
|
|
11
12
|
route?: string;
|
|
@@ -15,6 +16,7 @@ interface IWritableTagNames {
|
|
|
15
16
|
layoutOutlet?: string;
|
|
16
17
|
link?: string;
|
|
17
18
|
head?: string;
|
|
19
|
+
guardHandler?: string;
|
|
18
20
|
}
|
|
19
21
|
interface IConfig {
|
|
20
22
|
readonly tagNames: ITagNames;
|
|
@@ -112,6 +114,116 @@ interface IOutlet {
|
|
|
112
114
|
lastRoutes: IRoute[];
|
|
113
115
|
}
|
|
114
116
|
|
|
117
|
+
/**
|
|
118
|
+
* AppRoutes - Root component for @wcstack/router
|
|
119
|
+
*
|
|
120
|
+
* Container element that manages route definitions and navigation.
|
|
121
|
+
*/
|
|
122
|
+
declare class Router extends HTMLElement implements IRouter {
|
|
123
|
+
static wcBindable: IWcBindable;
|
|
124
|
+
private static _instance;
|
|
125
|
+
private _outlet;
|
|
126
|
+
private _template;
|
|
127
|
+
private _routeChildNodes;
|
|
128
|
+
private _basename;
|
|
129
|
+
private _path;
|
|
130
|
+
private _initialized;
|
|
131
|
+
private _fallbackRoute;
|
|
132
|
+
private _listeningPopState;
|
|
133
|
+
private _navigateUrl;
|
|
134
|
+
constructor();
|
|
135
|
+
/**
|
|
136
|
+
* Normalize a URL pathname to a route path.
|
|
137
|
+
* - ensure leading slash
|
|
138
|
+
* - collapse multiple slashes
|
|
139
|
+
* - treat trailing file extensions (e.g. .html) as directory root
|
|
140
|
+
* - remove trailing slash except root "/"
|
|
141
|
+
*/
|
|
142
|
+
private _normalizePathname;
|
|
143
|
+
/**
|
|
144
|
+
* Normalize basename.
|
|
145
|
+
* - "" or "/" -> ""
|
|
146
|
+
* - "/app/" -> "/app"
|
|
147
|
+
* - "/app/index.html" -> "/app"
|
|
148
|
+
*/
|
|
149
|
+
private _normalizeBasename;
|
|
150
|
+
private _joinInternalPath;
|
|
151
|
+
private _notifyLocationChange;
|
|
152
|
+
private _getBasename;
|
|
153
|
+
static get instance(): IRouter;
|
|
154
|
+
static navigate(path: string): void;
|
|
155
|
+
get basename(): string;
|
|
156
|
+
private _getOutlet;
|
|
157
|
+
private _getTemplate;
|
|
158
|
+
get outlet(): IOutlet;
|
|
159
|
+
get template(): HTMLTemplateElement;
|
|
160
|
+
get routeChildNodes(): IRoute[];
|
|
161
|
+
get path(): string;
|
|
162
|
+
/**
|
|
163
|
+
* applyRoute 内で設定される値です。
|
|
164
|
+
*/
|
|
165
|
+
set path(value: string);
|
|
166
|
+
get fallbackRoute(): IRoute | null;
|
|
167
|
+
/**
|
|
168
|
+
* Routeのfallback属性がある場合にそのルートを設定します。
|
|
169
|
+
*/
|
|
170
|
+
set fallbackRoute(value: IRoute | null);
|
|
171
|
+
get navigateUrl(): string | null;
|
|
172
|
+
set navigateUrl(value: string | null);
|
|
173
|
+
navigate(path: string): Promise<void>;
|
|
174
|
+
private _onNavigateFunc;
|
|
175
|
+
private _onNavigate;
|
|
176
|
+
private _onPopState;
|
|
177
|
+
private _initialize;
|
|
178
|
+
connectedCallback(): Promise<void>;
|
|
179
|
+
disconnectedCallback(): void;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
declare class Route extends HTMLElement implements IRoute {
|
|
183
|
+
static wcBindable: IWcBindable;
|
|
184
|
+
private _core;
|
|
185
|
+
private _routeParentNode;
|
|
186
|
+
private _routeChildNodes;
|
|
187
|
+
private _routerNode;
|
|
188
|
+
private _uuid;
|
|
189
|
+
private _placeHolder;
|
|
190
|
+
private _childNodeArray;
|
|
191
|
+
private _childIndex;
|
|
192
|
+
private _initialized;
|
|
193
|
+
constructor();
|
|
194
|
+
get routeParentNode(): IRoute | null;
|
|
195
|
+
get routeChildNodes(): IRoute[];
|
|
196
|
+
get routerNode(): IRouter;
|
|
197
|
+
get uuid(): string;
|
|
198
|
+
get placeHolder(): Comment;
|
|
199
|
+
get childNodeArray(): Node[];
|
|
200
|
+
get routes(): IRoute[];
|
|
201
|
+
get childIndex(): number;
|
|
202
|
+
get path(): string;
|
|
203
|
+
get name(): string;
|
|
204
|
+
get isRelative(): boolean;
|
|
205
|
+
get absolutePath(): string;
|
|
206
|
+
get segmentInfos(): ISegmentInfo[];
|
|
207
|
+
get absoluteSegmentInfos(): ISegmentInfo[];
|
|
208
|
+
get params(): Record<string, string>;
|
|
209
|
+
get typedParams(): Record<string, any>;
|
|
210
|
+
get paramNames(): string[];
|
|
211
|
+
get absoluteParamNames(): string[];
|
|
212
|
+
get weight(): number;
|
|
213
|
+
get absoluteWeight(): number;
|
|
214
|
+
get segmentCount(): number;
|
|
215
|
+
get absoluteSegmentCount(): number;
|
|
216
|
+
get fullpath(): string;
|
|
217
|
+
get guardHandler(): GuardHandler;
|
|
218
|
+
set guardHandler(value: GuardHandler);
|
|
219
|
+
setParams(params: Record<string, string>, typedParams: Record<string, any>): void;
|
|
220
|
+
clearParams(): void;
|
|
221
|
+
shouldChange(newParams: Record<string, string>): boolean;
|
|
222
|
+
guardCheck(matchResult: IRouteMatchResult): Promise<void>;
|
|
223
|
+
testAncestorNode(ancestorNode: IRoute): boolean;
|
|
224
|
+
initialize(routerNode: IRouter, routeParentNode: IRoute | null): void;
|
|
225
|
+
}
|
|
226
|
+
|
|
115
227
|
interface RouteParseOptions {
|
|
116
228
|
isIndex?: boolean;
|
|
117
229
|
isFallback?: boolean;
|
|
@@ -170,5 +282,5 @@ declare class RouteCore extends EventTarget {
|
|
|
170
282
|
guardCheck(matchResult: IRouteMatchResult): Promise<void>;
|
|
171
283
|
}
|
|
172
284
|
|
|
173
|
-
export { RouteCore, bootstrapRouter, getConfig };
|
|
285
|
+
export { Route, RouteCore, Router, bootstrapRouter, getConfig };
|
|
174
286
|
export type { IWritableConfig, IWritableTagNames, RouteParseOptions };
|
package/dist/index.esm.js
CHANGED
|
@@ -6,7 +6,8 @@ const _config = {
|
|
|
6
6
|
layout: "wcs-layout",
|
|
7
7
|
layoutOutlet: "wcs-layout-outlet",
|
|
8
8
|
link: "wcs-link",
|
|
9
|
-
head: "wcs-head"
|
|
9
|
+
head: "wcs-head",
|
|
10
|
+
guardHandler: "wcs-guard-handler"
|
|
10
11
|
},
|
|
11
12
|
enableShadowRoot: false,
|
|
12
13
|
basenameFileExtensions: [".html"]
|
|
@@ -909,6 +910,41 @@ function createLayoutOutlet() {
|
|
|
909
910
|
return document.createElement(config.tagNames.layoutOutlet);
|
|
910
911
|
}
|
|
911
912
|
|
|
913
|
+
async function importModule(script) {
|
|
914
|
+
let scriptModule = null;
|
|
915
|
+
const sourceComment = `\n//# sourceURL=wcs-guard-handler\n`;
|
|
916
|
+
const scriptText = script.text + sourceComment;
|
|
917
|
+
if (typeof URL.createObjectURL === 'function') {
|
|
918
|
+
const blob = new Blob([scriptText], { type: "application/javascript" });
|
|
919
|
+
const url = URL.createObjectURL(blob);
|
|
920
|
+
try {
|
|
921
|
+
scriptModule = await import(url);
|
|
922
|
+
}
|
|
923
|
+
catch {
|
|
924
|
+
// Blob URL import failed (e.g. happy-dom), fall through to data: URL
|
|
925
|
+
}
|
|
926
|
+
finally {
|
|
927
|
+
URL.revokeObjectURL(url);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
if (!scriptModule) {
|
|
931
|
+
// Fallback: Base64 data: URL (for test environments)
|
|
932
|
+
const b64 = btoa(String.fromCodePoint(...new TextEncoder().encode(scriptText)));
|
|
933
|
+
scriptModule = await import(`data:application/javascript;base64,${b64}`);
|
|
934
|
+
}
|
|
935
|
+
if (scriptModule && typeof scriptModule.default === 'function') {
|
|
936
|
+
return scriptModule.default;
|
|
937
|
+
}
|
|
938
|
+
return null;
|
|
939
|
+
}
|
|
940
|
+
function loadGuardHandler(script, route) {
|
|
941
|
+
importModule(script).then(handler => {
|
|
942
|
+
if (handler) {
|
|
943
|
+
route.guardHandler = handler;
|
|
944
|
+
}
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
|
|
912
948
|
function _duplicateCheck(routesByPath, route) {
|
|
913
949
|
let routes = routesByPath.get(route.absolutePath);
|
|
914
950
|
if (!routes) {
|
|
@@ -951,6 +987,16 @@ async function _parseNode(routerNode, node, routes, map, routesByPath) {
|
|
|
951
987
|
appendNode = route.placeHolder;
|
|
952
988
|
element = route;
|
|
953
989
|
}
|
|
990
|
+
else if (tagName === config.tagNames.guardHandler) {
|
|
991
|
+
if (routes.length > 0) {
|
|
992
|
+
const route = routes[routes.length - 1];
|
|
993
|
+
const script = element.querySelector('script[type="module"]');
|
|
994
|
+
if (script) {
|
|
995
|
+
loadGuardHandler(script, route);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
continue;
|
|
999
|
+
}
|
|
954
1000
|
else if (tagName === config.tagNames.layout) {
|
|
955
1001
|
const childFragment = document.createDocumentFragment();
|
|
956
1002
|
// Move child nodes to fragment to avoid duplication of
|
|
@@ -1253,6 +1299,14 @@ function getNavigation() {
|
|
|
1253
1299
|
* Container element that manages route definitions and navigation.
|
|
1254
1300
|
*/
|
|
1255
1301
|
class Router extends HTMLElement {
|
|
1302
|
+
static wcBindable = {
|
|
1303
|
+
protocol: "wc-bindable",
|
|
1304
|
+
version: 1,
|
|
1305
|
+
properties: [
|
|
1306
|
+
{ name: "navigateUrl", event: "wcs-router:navigate-url-changed" },
|
|
1307
|
+
{ name: "path", event: "wcs-router:path-changed" },
|
|
1308
|
+
],
|
|
1309
|
+
};
|
|
1256
1310
|
static _instance = null;
|
|
1257
1311
|
_outlet = null;
|
|
1258
1312
|
_template = null;
|
|
@@ -1262,6 +1316,7 @@ class Router extends HTMLElement {
|
|
|
1262
1316
|
_initialized = false;
|
|
1263
1317
|
_fallbackRoute = null;
|
|
1264
1318
|
_listeningPopState = false;
|
|
1319
|
+
_navigateUrl = null;
|
|
1265
1320
|
constructor() {
|
|
1266
1321
|
super();
|
|
1267
1322
|
if (Router._instance) {
|
|
@@ -1387,7 +1442,14 @@ class Router extends HTMLElement {
|
|
|
1387
1442
|
* applyRoute 内で設定される値です。
|
|
1388
1443
|
*/
|
|
1389
1444
|
set path(value) {
|
|
1445
|
+
const changed = this._path !== value;
|
|
1390
1446
|
this._path = value;
|
|
1447
|
+
if (changed) {
|
|
1448
|
+
this.dispatchEvent(new CustomEvent("wcs-router:path-changed", {
|
|
1449
|
+
detail: value,
|
|
1450
|
+
bubbles: true,
|
|
1451
|
+
}));
|
|
1452
|
+
}
|
|
1391
1453
|
}
|
|
1392
1454
|
get fallbackRoute() {
|
|
1393
1455
|
return this._fallbackRoute;
|
|
@@ -1398,6 +1460,21 @@ class Router extends HTMLElement {
|
|
|
1398
1460
|
set fallbackRoute(value) {
|
|
1399
1461
|
this._fallbackRoute = value;
|
|
1400
1462
|
}
|
|
1463
|
+
get navigateUrl() {
|
|
1464
|
+
return this._navigateUrl;
|
|
1465
|
+
}
|
|
1466
|
+
set navigateUrl(value) {
|
|
1467
|
+
if (value === null || value === undefined || value === "")
|
|
1468
|
+
return;
|
|
1469
|
+
this._navigateUrl = value;
|
|
1470
|
+
this.navigate(value).then(() => {
|
|
1471
|
+
this._navigateUrl = null;
|
|
1472
|
+
this.dispatchEvent(new CustomEvent("wcs-router:navigate-url-changed", {
|
|
1473
|
+
detail: null,
|
|
1474
|
+
bubbles: true,
|
|
1475
|
+
}));
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1401
1478
|
async navigate(path) {
|
|
1402
1479
|
const fullPath = this._joinInternalPath(this._basename, path);
|
|
1403
1480
|
const navigation = getNavigation();
|
|
@@ -1832,5 +1909,5 @@ function bootstrapRouter(config) {
|
|
|
1832
1909
|
registerComponents();
|
|
1833
1910
|
}
|
|
1834
1911
|
|
|
1835
|
-
export { RouteCore, bootstrapRouter, getConfig };
|
|
1912
|
+
export { Route, RouteCore, Router, bootstrapRouter, getConfig };
|
|
1836
1913
|
//# sourceMappingURL=index.esm.js.map
|