@real-router/solid 0.5.2 → 0.7.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/README.md +15 -3
- package/dist/cjs/index.d.ts +20 -1
- package/dist/cjs/index.js +236 -18
- package/dist/esm/index.d.mts +20 -1
- package/dist/esm/index.mjs +236 -18
- package/dist/types/RouterProvider.d.ts +2 -0
- package/dist/types/RouterProvider.d.ts.map +1 -1
- package/dist/types/components/Link.d.ts.map +1 -1
- package/dist/types/components/RouteView/RouteView.d.ts +3 -2
- package/dist/types/components/RouteView/RouteView.d.ts.map +1 -1
- package/dist/types/components/RouteView/components.d.ts +12 -2
- package/dist/types/components/RouteView/components.d.ts.map +1 -1
- package/dist/types/components/RouteView/helpers.d.ts.map +1 -1
- package/dist/types/components/RouteView/index.d.ts +1 -1
- package/dist/types/components/RouteView/index.d.ts.map +1 -1
- package/dist/types/components/RouteView/types.d.ts +6 -0
- package/dist/types/components/RouteView/types.d.ts.map +1 -1
- package/dist/types/directives/link.d.ts.map +1 -1
- package/dist/types/dom-utils/index.d.ts +2 -0
- package/dist/types/dom-utils/index.d.ts.map +1 -1
- package/dist/types/dom-utils/scroll-restore.d.ts +11 -0
- package/dist/types/dom-utils/scroll-restore.d.ts.map +1 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/RouterProvider.tsx +15 -1
- package/src/components/Link.tsx +1 -5
- package/src/components/RouteView/RouteView.tsx +7 -2
- package/src/components/RouteView/components.tsx +25 -2
- package/src/components/RouteView/helpers.tsx +67 -21
- package/src/components/RouteView/index.ts +1 -0
- package/src/components/RouteView/types.ts +7 -0
- package/src/directives/link.tsx +1 -5
- package/src/index.tsx +1 -0
package/README.md
CHANGED
|
@@ -320,18 +320,30 @@ Enable screen reader announcements for route changes:
|
|
|
320
320
|
|
|
321
321
|
When enabled, a visually hidden `aria-live` region announces each navigation. Focus moves to the first `<h1>` on the new page. See [Accessibility guide](https://github.com/greydragon888/real-router/wiki/Accessibility) for details.
|
|
322
322
|
|
|
323
|
+
## Scroll Restoration
|
|
324
|
+
|
|
325
|
+
Opt-in preservation of scroll position across navigations:
|
|
326
|
+
|
|
327
|
+
```tsx
|
|
328
|
+
<RouterProvider router={router} scrollRestoration={{ mode: "restore" }}>
|
|
329
|
+
{/* Your app */}
|
|
330
|
+
</RouterProvider>
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Restores scroll on back/forward, scrolls to top (or `#hash`) on push. Three modes: `"restore"` (default), `"top"`, `"manual"`. Custom containers via `scrollContainer: () => HTMLElement | null`. Options are read once on mount — changing the prop at runtime does not reconfigure the utility (Solid `onMount` is non-reactive). See [Scroll Restoration guide](https://github.com/greydragon888/real-router/wiki/Scroll-Restoration) for details.
|
|
334
|
+
|
|
323
335
|
## Documentation
|
|
324
336
|
|
|
325
337
|
Full documentation: [Wiki](https://github.com/greydragon888/real-router/wiki)
|
|
326
338
|
|
|
327
|
-
- [RouterProvider](https://github.com/greydragon888/real-router/wiki/RouterProvider) · [RouteView](https://github.com/greydragon888/real-router/wiki/RouteView) · [RouterErrorBoundary](https://github.com/greydragon888/real-router/wiki/RouterErrorBoundary) · [Link](https://github.com/greydragon888/real-router/wiki/Link)
|
|
339
|
+
- [RouterProvider](https://github.com/greydragon888/real-router/wiki/RouterProvider) · [RouteView](https://github.com/greydragon888/real-router/wiki/RouteView) · [RouterErrorBoundary](https://github.com/greydragon888/real-router/wiki/RouterErrorBoundary) · [Link](https://github.com/greydragon888/real-router/wiki/Link) · [Scroll Restoration](https://github.com/greydragon888/real-router/wiki/Scroll-Restoration)
|
|
328
340
|
- [useRouter](https://github.com/greydragon888/real-router/wiki/useRouter) · [useRoute](https://github.com/greydragon888/real-router/wiki/useRoute) · [useRouteNode](https://github.com/greydragon888/real-router/wiki/useRouteNode) · [useNavigator](https://github.com/greydragon888/real-router/wiki/useNavigator) · [useRouteUtils](https://github.com/greydragon888/real-router/wiki/useRouteUtils) · [useRouterTransition](https://github.com/greydragon888/real-router/wiki/useRouterTransition)
|
|
329
341
|
|
|
330
342
|
## Examples
|
|
331
343
|
|
|
332
|
-
14 runnable examples — each is a standalone Vite app. Run: `cd examples/solid/basic && pnpm dev`
|
|
344
|
+
14 runnable examples — each is a standalone Vite app. Run: `cd examples/web/solid/basic && pnpm dev`
|
|
333
345
|
|
|
334
|
-
[basic](../../examples/solid/basic) · [nested-routes](../../examples/solid/nested-routes) · [auth-guards](../../examples/solid/auth-guards) · [data-loading](../../examples/solid/data-loading) · [lazy-loading](../../examples/solid/lazy-loading) · [async-guards](../../examples/solid/async-guards) · [hash-routing](../../examples/solid/hash-routing) · [persistent-params](../../examples/solid/persistent-params) · [error-handling](../../examples/solid/error-handling) · [dynamic-routes](../../examples/solid/dynamic-routes) · [store-based-state](../../examples/solid/store-based-state) · [use-link-directive](../../examples/solid/use-link-directive) · [signal-primitives](../../examples/solid/signal-primitives) · [combined](../../examples/solid/combined)
|
|
346
|
+
[basic](../../examples/web/solid/basic) · [nested-routes](../../examples/web/solid/nested-routes) · [auth-guards](../../examples/web/solid/auth-guards) · [data-loading](../../examples/web/solid/data-loading) · [lazy-loading](../../examples/web/solid/lazy-loading) · [async-guards](../../examples/web/solid/async-guards) · [hash-routing](../../examples/web/solid/hash-routing) · [persistent-params](../../examples/web/solid/persistent-params) · [error-handling](../../examples/web/solid/error-handling) · [dynamic-routes](../../examples/web/solid/dynamic-routes) · [store-based-state](../../examples/web/solid/store-based-state) · [use-link-directive](../../examples/web/solid/use-link-directive) · [signal-primitives](../../examples/web/solid/signal-primitives) · [combined](../../examples/web/solid/combined)
|
|
335
347
|
|
|
336
348
|
## Related Packages
|
|
337
349
|
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -17,6 +17,12 @@ interface MatchProps {
|
|
|
17
17
|
readonly fallback?: JSX.Element;
|
|
18
18
|
readonly children: JSX.Element;
|
|
19
19
|
}
|
|
20
|
+
interface SelfProps {
|
|
21
|
+
/** Fallback content while children are suspended. */
|
|
22
|
+
readonly fallback?: JSX.Element;
|
|
23
|
+
/** Content to render when the active route name equals the parent RouteView's nodeName. */
|
|
24
|
+
readonly children: JSX.Element;
|
|
25
|
+
}
|
|
20
26
|
interface NotFoundProps {
|
|
21
27
|
readonly children: JSX.Element;
|
|
22
28
|
}
|
|
@@ -25,6 +31,10 @@ declare function Match(props: MatchProps): JSX.Element;
|
|
|
25
31
|
declare namespace Match {
|
|
26
32
|
var displayName: string;
|
|
27
33
|
}
|
|
34
|
+
declare function Self(props: SelfProps): JSX.Element;
|
|
35
|
+
declare namespace Self {
|
|
36
|
+
var displayName: string;
|
|
37
|
+
}
|
|
28
38
|
declare function NotFound(props: NotFoundProps): JSX.Element;
|
|
29
39
|
declare namespace NotFound {
|
|
30
40
|
var displayName: string;
|
|
@@ -36,6 +46,7 @@ declare namespace RouteViewRoot {
|
|
|
36
46
|
}
|
|
37
47
|
declare const RouteView: typeof RouteViewRoot & {
|
|
38
48
|
Match: typeof Match;
|
|
49
|
+
Self: typeof Self;
|
|
39
50
|
NotFound: typeof NotFound;
|
|
40
51
|
};
|
|
41
52
|
|
|
@@ -89,9 +100,17 @@ declare function useRouteNodeStore(nodeName: string): RouteState;
|
|
|
89
100
|
|
|
90
101
|
declare function useRouterTransition(): Accessor<RouterTransitionSnapshot>;
|
|
91
102
|
|
|
103
|
+
type ScrollRestorationMode = "restore" | "top" | "manual";
|
|
104
|
+
interface ScrollRestorationOptions {
|
|
105
|
+
mode?: ScrollRestorationMode | undefined;
|
|
106
|
+
anchorScrolling?: boolean | undefined;
|
|
107
|
+
scrollContainer?: (() => HTMLElement | null) | undefined;
|
|
108
|
+
}
|
|
109
|
+
|
|
92
110
|
interface RouteProviderProps {
|
|
93
111
|
router: Router;
|
|
94
112
|
announceNavigation?: boolean;
|
|
113
|
+
scrollRestoration?: ScrollRestorationOptions;
|
|
95
114
|
}
|
|
96
115
|
declare function RouterProvider(props: ParentProps<RouteProviderProps>): JSX.Element;
|
|
97
116
|
|
|
@@ -108,4 +127,4 @@ declare function createSignalFromSource<T>(source: RouterSource<T>): Accessor<T>
|
|
|
108
127
|
declare function createStoreFromSource<T extends object>(source: RouterSource<T>): T;
|
|
109
128
|
|
|
110
129
|
export { Link, RouteContext, RouteView, RouterContext, RouterErrorBoundary, RouterProvider, createSignalFromSource, createStoreFromSource, link, useNavigator, useRoute, useRouteNode, useRouteNodeStore, useRouteStore, useRouteUtils, useRouter, useRouterTransition };
|
|
111
|
-
export type { LinkDirectiveOptions, LinkProps, RouteState, MatchProps as RouteViewMatchProps, NotFoundProps as RouteViewNotFoundProps, RouteViewProps, RouterErrorBoundaryProps };
|
|
130
|
+
export type { LinkDirectiveOptions, LinkProps, RouteState, MatchProps as RouteViewMatchProps, NotFoundProps as RouteViewNotFoundProps, RouteViewProps, SelfProps as RouteViewSelfProps, RouterErrorBoundaryProps };
|
package/dist/cjs/index.js
CHANGED
|
@@ -11,6 +11,7 @@ var core = require('@real-router/core');
|
|
|
11
11
|
// Local (non-global) Symbols — Symbol.for() would expose markers to spoofing
|
|
12
12
|
// via the global Symbol registry. See Gotchas section "RouteView Marker Objects".
|
|
13
13
|
const MATCH_MARKER = Symbol("RouteView.Match");
|
|
14
|
+
const SELF_MARKER = Symbol("RouteView.Self");
|
|
14
15
|
const NOT_FOUND_MARKER = Symbol("RouteView.NotFound");
|
|
15
16
|
function Match(props) {
|
|
16
17
|
const result = {
|
|
@@ -29,6 +30,19 @@ function Match(props) {
|
|
|
29
30
|
return result;
|
|
30
31
|
}
|
|
31
32
|
Match.displayName = "RouteView.Match";
|
|
33
|
+
function Self(props) {
|
|
34
|
+
const result = {
|
|
35
|
+
$$type: SELF_MARKER,
|
|
36
|
+
fallback: props.fallback,
|
|
37
|
+
get children() {
|
|
38
|
+
return props.children;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// See Match for the marker-pattern rationale.
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
Self.displayName = "RouteView.Self";
|
|
32
46
|
function NotFound(props) {
|
|
33
47
|
const result = {
|
|
34
48
|
$$type: NOT_FOUND_MARKER,
|
|
@@ -51,6 +65,9 @@ function isSegmentMatch(routeName, fullSegmentName, exact) {
|
|
|
51
65
|
function isMatchMarker(value) {
|
|
52
66
|
return value != null && typeof value === "object" && "$$type" in value && value.$$type === MATCH_MARKER;
|
|
53
67
|
}
|
|
68
|
+
function isSelfMarker(value) {
|
|
69
|
+
return value != null && typeof value === "object" && "$$type" in value && value.$$type === SELF_MARKER;
|
|
70
|
+
}
|
|
54
71
|
function isNotFoundMarker(value) {
|
|
55
72
|
return value != null && typeof value === "object" && "$$type" in value && value.$$type === NOT_FOUND_MARKER;
|
|
56
73
|
}
|
|
@@ -64,11 +81,48 @@ function collectElements(children, result) {
|
|
|
64
81
|
}
|
|
65
82
|
return;
|
|
66
83
|
}
|
|
67
|
-
if (isMatchMarker(children) || isNotFoundMarker(children)) {
|
|
84
|
+
if (isMatchMarker(children) || isSelfMarker(children) || isNotFoundMarker(children)) {
|
|
68
85
|
result.push(children);
|
|
69
86
|
}
|
|
70
87
|
}
|
|
88
|
+
|
|
89
|
+
// child.children is a getter — read it INSIDE the JSX expression so Solid
|
|
90
|
+
// creates a reactive dependency. Pulling it into a variable freezes the
|
|
91
|
+
// value at template-build time and breaks Suspense fallback transitions
|
|
92
|
+
// (lazy() resolution).
|
|
93
|
+
function renderMatch(child) {
|
|
94
|
+
return child.fallback === undefined ? child.children : web.createComponent(solidJs.Suspense, {
|
|
95
|
+
get fallback() {
|
|
96
|
+
return child.fallback;
|
|
97
|
+
},
|
|
98
|
+
get children() {
|
|
99
|
+
return child.children;
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
function renderSelf(self) {
|
|
104
|
+
return self.fallback === undefined ? self.children : web.createComponent(solidJs.Suspense, {
|
|
105
|
+
get fallback() {
|
|
106
|
+
return self.fallback;
|
|
107
|
+
},
|
|
108
|
+
get children() {
|
|
109
|
+
return self.children;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function processMatchChild(child, routeName, nodeName) {
|
|
114
|
+
const {
|
|
115
|
+
segment,
|
|
116
|
+
exact
|
|
117
|
+
} = child;
|
|
118
|
+
const fullSegmentName = nodeName ? `${nodeName}.${segment}` : segment;
|
|
119
|
+
if (!isSegmentMatch(routeName, fullSegmentName, exact)) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
return renderMatch(child);
|
|
123
|
+
}
|
|
71
124
|
function buildRenderList(elements, routeName, nodeName) {
|
|
125
|
+
let selfMarker = null;
|
|
72
126
|
let notFoundChildren = null;
|
|
73
127
|
let activeMatchFound = false;
|
|
74
128
|
const rendered = [];
|
|
@@ -77,28 +131,25 @@ function buildRenderList(elements, routeName, nodeName) {
|
|
|
77
131
|
notFoundChildren = child.children;
|
|
78
132
|
continue;
|
|
79
133
|
}
|
|
80
|
-
if (
|
|
134
|
+
if (isSelfMarker(child)) {
|
|
135
|
+
selfMarker ??= child;
|
|
81
136
|
continue;
|
|
82
137
|
}
|
|
83
|
-
|
|
84
|
-
segment,
|
|
85
|
-
exact,
|
|
86
|
-
fallback
|
|
87
|
-
} = child;
|
|
88
|
-
const fullSegmentName = nodeName ? `${nodeName}.${segment}` : segment;
|
|
89
|
-
if (!isSegmentMatch(routeName, fullSegmentName, exact)) {
|
|
138
|
+
if (activeMatchFound) {
|
|
90
139
|
continue;
|
|
91
140
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
}));
|
|
141
|
+
const matchRendered = processMatchChild(child, routeName, nodeName);
|
|
142
|
+
if (matchRendered !== null) {
|
|
143
|
+
activeMatchFound = true;
|
|
144
|
+
rendered.push(matchRendered);
|
|
145
|
+
}
|
|
99
146
|
}
|
|
100
|
-
if (!activeMatchFound
|
|
101
|
-
|
|
147
|
+
if (!activeMatchFound) {
|
|
148
|
+
if (selfMarker !== null && routeName === nodeName) {
|
|
149
|
+
rendered.push(renderSelf(selfMarker));
|
|
150
|
+
} else if (routeName === core.UNKNOWN_ROUTE && notFoundChildren !== null) {
|
|
151
|
+
rendered.push(notFoundChildren);
|
|
152
|
+
}
|
|
102
153
|
}
|
|
103
154
|
return rendered;
|
|
104
155
|
}
|
|
@@ -157,6 +208,7 @@ function RouteViewRoot(props) {
|
|
|
157
208
|
RouteViewRoot.displayName = "RouteView";
|
|
158
209
|
const RouteView = Object.assign(RouteViewRoot, {
|
|
159
210
|
Match,
|
|
211
|
+
Self,
|
|
160
212
|
NotFound
|
|
161
213
|
});
|
|
162
214
|
|
|
@@ -284,6 +336,163 @@ function manageFocus(h1) {
|
|
|
284
336
|
});
|
|
285
337
|
}
|
|
286
338
|
|
|
339
|
+
const STORAGE_KEY = "real-router:scroll";
|
|
340
|
+
const NOOP_INSTANCE = Object.freeze({
|
|
341
|
+
destroy: () => {
|
|
342
|
+
/* no-op */
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
function createScrollRestoration(router, options) {
|
|
346
|
+
if (typeof globalThis.window === "undefined") {
|
|
347
|
+
return NOOP_INSTANCE;
|
|
348
|
+
}
|
|
349
|
+
const mode = options?.mode ?? "restore";
|
|
350
|
+
|
|
351
|
+
// mode "manual" = utility does nothing. Don't flip history.scrollRestoration,
|
|
352
|
+
// don't subscribe, don't register pagehide — leave the browser's native
|
|
353
|
+
// auto-restore intact for the app to override if it wants to.
|
|
354
|
+
if (mode === "manual") {
|
|
355
|
+
return NOOP_INSTANCE;
|
|
356
|
+
}
|
|
357
|
+
const anchorEnabled = options?.anchorScrolling ?? true;
|
|
358
|
+
const getContainer = options?.scrollContainer;
|
|
359
|
+
const prevScrollRestoration = history.scrollRestoration;
|
|
360
|
+
try {
|
|
361
|
+
history.scrollRestoration = "manual";
|
|
362
|
+
} catch {
|
|
363
|
+
// Ignore — some embedded contexts may reject the assignment.
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Resolve the container lazily on every event so containers mounted AFTER
|
|
367
|
+
// the provider still get correct scroll handling. Falls back to window when
|
|
368
|
+
// the getter is absent or returns null (pre-mount).
|
|
369
|
+
const readPos = () => {
|
|
370
|
+
const element = getContainer?.();
|
|
371
|
+
return element ? element.scrollTop : globalThis.scrollY;
|
|
372
|
+
};
|
|
373
|
+
const writePos = top => {
|
|
374
|
+
const element = getContainer?.();
|
|
375
|
+
if (element) {
|
|
376
|
+
element.scrollTop = top;
|
|
377
|
+
} else {
|
|
378
|
+
globalThis.scrollTo(0, top);
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
const scrollToHashOrTop = () => {
|
|
382
|
+
const hash = globalThis.location.hash;
|
|
383
|
+
if (anchorEnabled && hash.length > 1) {
|
|
384
|
+
// location.hash is percent-encoded; ids in the DOM are the raw string.
|
|
385
|
+
// Decode for the match. Fall back to the raw slice if the hash contains
|
|
386
|
+
// a malformed escape sequence (decodeURIComponent throws on those).
|
|
387
|
+
let id;
|
|
388
|
+
try {
|
|
389
|
+
id = decodeURIComponent(hash.slice(1));
|
|
390
|
+
} catch {
|
|
391
|
+
id = hash.slice(1);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// eslint-disable-next-line unicorn/prefer-query-selector -- ids may contain CSS-unsafe chars
|
|
395
|
+
const element = document.getElementById(id);
|
|
396
|
+
if (element) {
|
|
397
|
+
element.scrollIntoView();
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
writePos(0);
|
|
402
|
+
};
|
|
403
|
+
let destroyed = false;
|
|
404
|
+
const unsubscribe = router.subscribe(({
|
|
405
|
+
route,
|
|
406
|
+
previousRoute
|
|
407
|
+
}) => {
|
|
408
|
+
const nav = route.context.navigation;
|
|
409
|
+
|
|
410
|
+
// Browsers dispatch reload as the initial navigation after refresh, so
|
|
411
|
+
// previousRoute is undefined and capture is naturally skipped. The
|
|
412
|
+
// pre-refresh position was already persisted via pagehide.
|
|
413
|
+
if (previousRoute) {
|
|
414
|
+
putPos(keyOf(previousRoute), readPos());
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Single rAF so DOM is committed before we read anchors / write scroll.
|
|
418
|
+
// Guard against destroy() racing with the callback.
|
|
419
|
+
requestAnimationFrame(() => {
|
|
420
|
+
if (destroyed) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
if (mode === "top" || !nav) {
|
|
424
|
+
scrollToHashOrTop();
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
if (nav.navigationType === "replace") {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
if (nav.direction === "back" || nav.navigationType === "traverse" || nav.navigationType === "reload") {
|
|
431
|
+
writePos(loadStore()[keyOf(route)] ?? 0);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
scrollToHashOrTop();
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
const onPageHide = () => {
|
|
438
|
+
const current = router.getState();
|
|
439
|
+
if (current) {
|
|
440
|
+
putPos(keyOf(current), readPos());
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
globalThis.addEventListener("pagehide", onPageHide);
|
|
444
|
+
return {
|
|
445
|
+
destroy: () => {
|
|
446
|
+
if (destroyed) {
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
destroyed = true;
|
|
450
|
+
unsubscribe();
|
|
451
|
+
globalThis.removeEventListener("pagehide", onPageHide);
|
|
452
|
+
try {
|
|
453
|
+
history.scrollRestoration = prevScrollRestoration;
|
|
454
|
+
} catch {
|
|
455
|
+
// Ignore.
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
function keyOf(state) {
|
|
461
|
+
return `${state.name}:${canonicalJson(state.params)}`;
|
|
462
|
+
}
|
|
463
|
+
function loadStore() {
|
|
464
|
+
try {
|
|
465
|
+
const raw = sessionStorage.getItem(STORAGE_KEY);
|
|
466
|
+
return raw ? JSON.parse(raw) : {};
|
|
467
|
+
} catch {
|
|
468
|
+
return {};
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
function putPos(key, pos) {
|
|
472
|
+
try {
|
|
473
|
+
const store = loadStore();
|
|
474
|
+
store[key] = pos;
|
|
475
|
+
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(store));
|
|
476
|
+
} catch {
|
|
477
|
+
// Ignore quota / security errors.
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
function canonicalJson(value) {
|
|
481
|
+
return JSON.stringify(value, canonicalReplacer);
|
|
482
|
+
}
|
|
483
|
+
function canonicalReplacer(_key, val) {
|
|
484
|
+
if (val !== null && typeof val === "object" && !Array.isArray(val)) {
|
|
485
|
+
const sorted = {};
|
|
486
|
+
// eslint-disable-next-line unicorn/no-array-sort -- ng-packagr uses pre-ES2023 lib; toSorted unavailable
|
|
487
|
+
const keys = Object.keys(val).sort((left, right) => left.localeCompare(right));
|
|
488
|
+
for (const key of keys) {
|
|
489
|
+
sorted[key] = val[key];
|
|
490
|
+
}
|
|
491
|
+
return sorted;
|
|
492
|
+
}
|
|
493
|
+
return val;
|
|
494
|
+
}
|
|
495
|
+
|
|
287
496
|
function shouldNavigate(evt) {
|
|
288
497
|
return evt.button === 0 && !evt.metaKey && !evt.altKey && !evt.ctrlKey && !evt.shiftKey;
|
|
289
498
|
}
|
|
@@ -514,6 +723,15 @@ function RouterProvider(props) {
|
|
|
514
723
|
announcer.destroy();
|
|
515
724
|
});
|
|
516
725
|
});
|
|
726
|
+
solidJs.onMount(() => {
|
|
727
|
+
if (!props.scrollRestoration) {
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
const sr = createScrollRestoration(props.router, props.scrollRestoration);
|
|
731
|
+
solidJs.onCleanup(() => {
|
|
732
|
+
sr.destroy();
|
|
733
|
+
});
|
|
734
|
+
});
|
|
517
735
|
const navigator = core.getNavigator(props.router);
|
|
518
736
|
const routeSource = sources.createRouteSource(props.router);
|
|
519
737
|
const routeSignal = createSignalFromSource(routeSource);
|
package/dist/esm/index.d.mts
CHANGED
|
@@ -17,6 +17,12 @@ interface MatchProps {
|
|
|
17
17
|
readonly fallback?: JSX.Element;
|
|
18
18
|
readonly children: JSX.Element;
|
|
19
19
|
}
|
|
20
|
+
interface SelfProps {
|
|
21
|
+
/** Fallback content while children are suspended. */
|
|
22
|
+
readonly fallback?: JSX.Element;
|
|
23
|
+
/** Content to render when the active route name equals the parent RouteView's nodeName. */
|
|
24
|
+
readonly children: JSX.Element;
|
|
25
|
+
}
|
|
20
26
|
interface NotFoundProps {
|
|
21
27
|
readonly children: JSX.Element;
|
|
22
28
|
}
|
|
@@ -25,6 +31,10 @@ declare function Match(props: MatchProps): JSX.Element;
|
|
|
25
31
|
declare namespace Match {
|
|
26
32
|
var displayName: string;
|
|
27
33
|
}
|
|
34
|
+
declare function Self(props: SelfProps): JSX.Element;
|
|
35
|
+
declare namespace Self {
|
|
36
|
+
var displayName: string;
|
|
37
|
+
}
|
|
28
38
|
declare function NotFound(props: NotFoundProps): JSX.Element;
|
|
29
39
|
declare namespace NotFound {
|
|
30
40
|
var displayName: string;
|
|
@@ -36,6 +46,7 @@ declare namespace RouteViewRoot {
|
|
|
36
46
|
}
|
|
37
47
|
declare const RouteView: typeof RouteViewRoot & {
|
|
38
48
|
Match: typeof Match;
|
|
49
|
+
Self: typeof Self;
|
|
39
50
|
NotFound: typeof NotFound;
|
|
40
51
|
};
|
|
41
52
|
|
|
@@ -89,9 +100,17 @@ declare function useRouteNodeStore(nodeName: string): RouteState;
|
|
|
89
100
|
|
|
90
101
|
declare function useRouterTransition(): Accessor<RouterTransitionSnapshot>;
|
|
91
102
|
|
|
103
|
+
type ScrollRestorationMode = "restore" | "top" | "manual";
|
|
104
|
+
interface ScrollRestorationOptions {
|
|
105
|
+
mode?: ScrollRestorationMode | undefined;
|
|
106
|
+
anchorScrolling?: boolean | undefined;
|
|
107
|
+
scrollContainer?: (() => HTMLElement | null) | undefined;
|
|
108
|
+
}
|
|
109
|
+
|
|
92
110
|
interface RouteProviderProps {
|
|
93
111
|
router: Router;
|
|
94
112
|
announceNavigation?: boolean;
|
|
113
|
+
scrollRestoration?: ScrollRestorationOptions;
|
|
95
114
|
}
|
|
96
115
|
declare function RouterProvider(props: ParentProps<RouteProviderProps>): JSX.Element;
|
|
97
116
|
|
|
@@ -108,4 +127,4 @@ declare function createSignalFromSource<T>(source: RouterSource<T>): Accessor<T>
|
|
|
108
127
|
declare function createStoreFromSource<T extends object>(source: RouterSource<T>): T;
|
|
109
128
|
|
|
110
129
|
export { Link, RouteContext, RouteView, RouterContext, RouterErrorBoundary, RouterProvider, createSignalFromSource, createStoreFromSource, link, useNavigator, useRoute, useRouteNode, useRouteNodeStore, useRouteStore, useRouteUtils, useRouter, useRouterTransition };
|
|
111
|
-
export type { LinkDirectiveOptions, LinkProps, RouteState, MatchProps as RouteViewMatchProps, NotFoundProps as RouteViewNotFoundProps, RouteViewProps, RouterErrorBoundaryProps };
|
|
130
|
+
export type { LinkDirectiveOptions, LinkProps, RouteState, MatchProps as RouteViewMatchProps, NotFoundProps as RouteViewNotFoundProps, RouteViewProps, SelfProps as RouteViewSelfProps, RouterErrorBoundaryProps };
|