@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.
Files changed (34) hide show
  1. package/README.md +15 -3
  2. package/dist/cjs/index.d.ts +20 -1
  3. package/dist/cjs/index.js +236 -18
  4. package/dist/esm/index.d.mts +20 -1
  5. package/dist/esm/index.mjs +236 -18
  6. package/dist/types/RouterProvider.d.ts +2 -0
  7. package/dist/types/RouterProvider.d.ts.map +1 -1
  8. package/dist/types/components/Link.d.ts.map +1 -1
  9. package/dist/types/components/RouteView/RouteView.d.ts +3 -2
  10. package/dist/types/components/RouteView/RouteView.d.ts.map +1 -1
  11. package/dist/types/components/RouteView/components.d.ts +12 -2
  12. package/dist/types/components/RouteView/components.d.ts.map +1 -1
  13. package/dist/types/components/RouteView/helpers.d.ts.map +1 -1
  14. package/dist/types/components/RouteView/index.d.ts +1 -1
  15. package/dist/types/components/RouteView/index.d.ts.map +1 -1
  16. package/dist/types/components/RouteView/types.d.ts +6 -0
  17. package/dist/types/components/RouteView/types.d.ts.map +1 -1
  18. package/dist/types/directives/link.d.ts.map +1 -1
  19. package/dist/types/dom-utils/index.d.ts +2 -0
  20. package/dist/types/dom-utils/index.d.ts.map +1 -1
  21. package/dist/types/dom-utils/scroll-restore.d.ts +11 -0
  22. package/dist/types/dom-utils/scroll-restore.d.ts.map +1 -0
  23. package/dist/types/index.d.ts +1 -1
  24. package/dist/types/index.d.ts.map +1 -1
  25. package/package.json +3 -3
  26. package/src/RouterProvider.tsx +15 -1
  27. package/src/components/Link.tsx +1 -5
  28. package/src/components/RouteView/RouteView.tsx +7 -2
  29. package/src/components/RouteView/components.tsx +25 -2
  30. package/src/components/RouteView/helpers.tsx +67 -21
  31. package/src/components/RouteView/index.ts +1 -0
  32. package/src/components/RouteView/types.ts +7 -0
  33. package/src/directives/link.tsx +1 -5
  34. package/src/index.tsx +1 -0
@@ -9,6 +9,7 @@ import { UNKNOWN_ROUTE, getNavigator } from '@real-router/core';
9
9
  // Local (non-global) Symbols — Symbol.for() would expose markers to spoofing
10
10
  // via the global Symbol registry. See Gotchas section "RouteView Marker Objects".
11
11
  const MATCH_MARKER = Symbol("RouteView.Match");
12
+ const SELF_MARKER = Symbol("RouteView.Self");
12
13
  const NOT_FOUND_MARKER = Symbol("RouteView.NotFound");
13
14
  function Match(props) {
14
15
  const result = {
@@ -27,6 +28,19 @@ function Match(props) {
27
28
  return result;
28
29
  }
29
30
  Match.displayName = "RouteView.Match";
31
+ function Self(props) {
32
+ const result = {
33
+ $$type: SELF_MARKER,
34
+ fallback: props.fallback,
35
+ get children() {
36
+ return props.children;
37
+ }
38
+ };
39
+
40
+ // See Match for the marker-pattern rationale.
41
+ return result;
42
+ }
43
+ Self.displayName = "RouteView.Self";
30
44
  function NotFound(props) {
31
45
  const result = {
32
46
  $$type: NOT_FOUND_MARKER,
@@ -49,6 +63,9 @@ function isSegmentMatch(routeName, fullSegmentName, exact) {
49
63
  function isMatchMarker(value) {
50
64
  return value != null && typeof value === "object" && "$$type" in value && value.$$type === MATCH_MARKER;
51
65
  }
66
+ function isSelfMarker(value) {
67
+ return value != null && typeof value === "object" && "$$type" in value && value.$$type === SELF_MARKER;
68
+ }
52
69
  function isNotFoundMarker(value) {
53
70
  return value != null && typeof value === "object" && "$$type" in value && value.$$type === NOT_FOUND_MARKER;
54
71
  }
@@ -62,11 +79,48 @@ function collectElements(children, result) {
62
79
  }
63
80
  return;
64
81
  }
65
- if (isMatchMarker(children) || isNotFoundMarker(children)) {
82
+ if (isMatchMarker(children) || isSelfMarker(children) || isNotFoundMarker(children)) {
66
83
  result.push(children);
67
84
  }
68
85
  }
86
+
87
+ // child.children is a getter — read it INSIDE the JSX expression so Solid
88
+ // creates a reactive dependency. Pulling it into a variable freezes the
89
+ // value at template-build time and breaks Suspense fallback transitions
90
+ // (lazy() resolution).
91
+ function renderMatch(child) {
92
+ return child.fallback === undefined ? child.children : createComponent(Suspense, {
93
+ get fallback() {
94
+ return child.fallback;
95
+ },
96
+ get children() {
97
+ return child.children;
98
+ }
99
+ });
100
+ }
101
+ function renderSelf(self) {
102
+ return self.fallback === undefined ? self.children : createComponent(Suspense, {
103
+ get fallback() {
104
+ return self.fallback;
105
+ },
106
+ get children() {
107
+ return self.children;
108
+ }
109
+ });
110
+ }
111
+ function processMatchChild(child, routeName, nodeName) {
112
+ const {
113
+ segment,
114
+ exact
115
+ } = child;
116
+ const fullSegmentName = nodeName ? `${nodeName}.${segment}` : segment;
117
+ if (!isSegmentMatch(routeName, fullSegmentName, exact)) {
118
+ return null;
119
+ }
120
+ return renderMatch(child);
121
+ }
69
122
  function buildRenderList(elements, routeName, nodeName) {
123
+ let selfMarker = null;
70
124
  let notFoundChildren = null;
71
125
  let activeMatchFound = false;
72
126
  const rendered = [];
@@ -75,28 +129,25 @@ function buildRenderList(elements, routeName, nodeName) {
75
129
  notFoundChildren = child.children;
76
130
  continue;
77
131
  }
78
- if (activeMatchFound) {
132
+ if (isSelfMarker(child)) {
133
+ selfMarker ??= child;
79
134
  continue;
80
135
  }
81
- const {
82
- segment,
83
- exact,
84
- fallback
85
- } = child;
86
- const fullSegmentName = nodeName ? `${nodeName}.${segment}` : segment;
87
- if (!isSegmentMatch(routeName, fullSegmentName, exact)) {
136
+ if (activeMatchFound) {
88
137
  continue;
89
138
  }
90
- activeMatchFound = true;
91
- rendered.push(fallback === undefined ? child.children : createComponent(Suspense, {
92
- fallback: fallback,
93
- get children() {
94
- return child.children;
95
- }
96
- }));
139
+ const matchRendered = processMatchChild(child, routeName, nodeName);
140
+ if (matchRendered !== null) {
141
+ activeMatchFound = true;
142
+ rendered.push(matchRendered);
143
+ }
97
144
  }
98
- if (!activeMatchFound && routeName === UNKNOWN_ROUTE && notFoundChildren !== null) {
99
- rendered.push(notFoundChildren);
145
+ if (!activeMatchFound) {
146
+ if (selfMarker !== null && routeName === nodeName) {
147
+ rendered.push(renderSelf(selfMarker));
148
+ } else if (routeName === UNKNOWN_ROUTE && notFoundChildren !== null) {
149
+ rendered.push(notFoundChildren);
150
+ }
100
151
  }
101
152
  return rendered;
102
153
  }
@@ -155,6 +206,7 @@ function RouteViewRoot(props) {
155
206
  RouteViewRoot.displayName = "RouteView";
156
207
  const RouteView = Object.assign(RouteViewRoot, {
157
208
  Match,
209
+ Self,
158
210
  NotFound
159
211
  });
160
212
 
@@ -282,6 +334,163 @@ function manageFocus(h1) {
282
334
  });
283
335
  }
284
336
 
337
+ const STORAGE_KEY = "real-router:scroll";
338
+ const NOOP_INSTANCE = Object.freeze({
339
+ destroy: () => {
340
+ /* no-op */
341
+ }
342
+ });
343
+ function createScrollRestoration(router, options) {
344
+ if (typeof globalThis.window === "undefined") {
345
+ return NOOP_INSTANCE;
346
+ }
347
+ const mode = options?.mode ?? "restore";
348
+
349
+ // mode "manual" = utility does nothing. Don't flip history.scrollRestoration,
350
+ // don't subscribe, don't register pagehide — leave the browser's native
351
+ // auto-restore intact for the app to override if it wants to.
352
+ if (mode === "manual") {
353
+ return NOOP_INSTANCE;
354
+ }
355
+ const anchorEnabled = options?.anchorScrolling ?? true;
356
+ const getContainer = options?.scrollContainer;
357
+ const prevScrollRestoration = history.scrollRestoration;
358
+ try {
359
+ history.scrollRestoration = "manual";
360
+ } catch {
361
+ // Ignore — some embedded contexts may reject the assignment.
362
+ }
363
+
364
+ // Resolve the container lazily on every event so containers mounted AFTER
365
+ // the provider still get correct scroll handling. Falls back to window when
366
+ // the getter is absent or returns null (pre-mount).
367
+ const readPos = () => {
368
+ const element = getContainer?.();
369
+ return element ? element.scrollTop : globalThis.scrollY;
370
+ };
371
+ const writePos = top => {
372
+ const element = getContainer?.();
373
+ if (element) {
374
+ element.scrollTop = top;
375
+ } else {
376
+ globalThis.scrollTo(0, top);
377
+ }
378
+ };
379
+ const scrollToHashOrTop = () => {
380
+ const hash = globalThis.location.hash;
381
+ if (anchorEnabled && hash.length > 1) {
382
+ // location.hash is percent-encoded; ids in the DOM are the raw string.
383
+ // Decode for the match. Fall back to the raw slice if the hash contains
384
+ // a malformed escape sequence (decodeURIComponent throws on those).
385
+ let id;
386
+ try {
387
+ id = decodeURIComponent(hash.slice(1));
388
+ } catch {
389
+ id = hash.slice(1);
390
+ }
391
+
392
+ // eslint-disable-next-line unicorn/prefer-query-selector -- ids may contain CSS-unsafe chars
393
+ const element = document.getElementById(id);
394
+ if (element) {
395
+ element.scrollIntoView();
396
+ return;
397
+ }
398
+ }
399
+ writePos(0);
400
+ };
401
+ let destroyed = false;
402
+ const unsubscribe = router.subscribe(({
403
+ route,
404
+ previousRoute
405
+ }) => {
406
+ const nav = route.context.navigation;
407
+
408
+ // Browsers dispatch reload as the initial navigation after refresh, so
409
+ // previousRoute is undefined and capture is naturally skipped. The
410
+ // pre-refresh position was already persisted via pagehide.
411
+ if (previousRoute) {
412
+ putPos(keyOf(previousRoute), readPos());
413
+ }
414
+
415
+ // Single rAF so DOM is committed before we read anchors / write scroll.
416
+ // Guard against destroy() racing with the callback.
417
+ requestAnimationFrame(() => {
418
+ if (destroyed) {
419
+ return;
420
+ }
421
+ if (mode === "top" || !nav) {
422
+ scrollToHashOrTop();
423
+ return;
424
+ }
425
+ if (nav.navigationType === "replace") {
426
+ return;
427
+ }
428
+ if (nav.direction === "back" || nav.navigationType === "traverse" || nav.navigationType === "reload") {
429
+ writePos(loadStore()[keyOf(route)] ?? 0);
430
+ return;
431
+ }
432
+ scrollToHashOrTop();
433
+ });
434
+ });
435
+ const onPageHide = () => {
436
+ const current = router.getState();
437
+ if (current) {
438
+ putPos(keyOf(current), readPos());
439
+ }
440
+ };
441
+ globalThis.addEventListener("pagehide", onPageHide);
442
+ return {
443
+ destroy: () => {
444
+ if (destroyed) {
445
+ return;
446
+ }
447
+ destroyed = true;
448
+ unsubscribe();
449
+ globalThis.removeEventListener("pagehide", onPageHide);
450
+ try {
451
+ history.scrollRestoration = prevScrollRestoration;
452
+ } catch {
453
+ // Ignore.
454
+ }
455
+ }
456
+ };
457
+ }
458
+ function keyOf(state) {
459
+ return `${state.name}:${canonicalJson(state.params)}`;
460
+ }
461
+ function loadStore() {
462
+ try {
463
+ const raw = sessionStorage.getItem(STORAGE_KEY);
464
+ return raw ? JSON.parse(raw) : {};
465
+ } catch {
466
+ return {};
467
+ }
468
+ }
469
+ function putPos(key, pos) {
470
+ try {
471
+ const store = loadStore();
472
+ store[key] = pos;
473
+ sessionStorage.setItem(STORAGE_KEY, JSON.stringify(store));
474
+ } catch {
475
+ // Ignore quota / security errors.
476
+ }
477
+ }
478
+ function canonicalJson(value) {
479
+ return JSON.stringify(value, canonicalReplacer);
480
+ }
481
+ function canonicalReplacer(_key, val) {
482
+ if (val !== null && typeof val === "object" && !Array.isArray(val)) {
483
+ const sorted = {};
484
+ // eslint-disable-next-line unicorn/no-array-sort -- ng-packagr uses pre-ES2023 lib; toSorted unavailable
485
+ const keys = Object.keys(val).sort((left, right) => left.localeCompare(right));
486
+ for (const key of keys) {
487
+ sorted[key] = val[key];
488
+ }
489
+ return sorted;
490
+ }
491
+ return val;
492
+ }
493
+
285
494
  function shouldNavigate(evt) {
286
495
  return evt.button === 0 && !evt.metaKey && !evt.altKey && !evt.ctrlKey && !evt.shiftKey;
287
496
  }
@@ -512,6 +721,15 @@ function RouterProvider(props) {
512
721
  announcer.destroy();
513
722
  });
514
723
  });
724
+ onMount(() => {
725
+ if (!props.scrollRestoration) {
726
+ return;
727
+ }
728
+ const sr = createScrollRestoration(props.router, props.scrollRestoration);
729
+ onCleanup(() => {
730
+ sr.destroy();
731
+ });
732
+ });
515
733
  const navigator = getNavigator(props.router);
516
734
  const routeSource = createRouteSource(props.router);
517
735
  const routeSignal = createSignalFromSource(routeSource);
@@ -1,8 +1,10 @@
1
+ import type { ScrollRestorationOptions } from "./dom-utils";
1
2
  import type { Router } from "@real-router/core";
2
3
  import type { ParentProps, JSX } from "solid-js";
3
4
  export interface RouteProviderProps {
4
5
  router: Router;
5
6
  announceNavigation?: boolean;
7
+ scrollRestoration?: ScrollRestorationOptions;
6
8
  }
7
9
  export declare function isRouteActive(linkRouteName: string, currentRouteName: string): boolean;
8
10
  export declare function RouterProvider(props: ParentProps<RouteProviderProps>): JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"RouterProvider.d.ts","sourceRoot":"","sources":["../../src/RouterProvider.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAEjD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,wBAAgB,aAAa,CAC3B,aAAa,EAAE,MAAM,EACrB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAKT;AAED,wBAAgB,cAAc,CAC5B,KAAK,EAAE,WAAW,CAAC,kBAAkB,CAAC,GACrC,GAAG,CAAC,OAAO,CA+Bb"}
1
+ {"version":3,"file":"RouterProvider.d.ts","sourceRoot":"","sources":["../../src/RouterProvider.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAEjD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;CAC9C;AAED,wBAAgB,aAAa,CAC3B,aAAa,EAAE,MAAM,EACrB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAKT;AAED,wBAAgB,cAAc,CAC5B,KAAK,EAAE,WAAW,CAAC,kBAAkB,CAAC,GACrC,GAAG,CAAC,OAAO,CA2Cb"}
@@ -1 +1 @@
1
- {"version":3,"file":"Link.d.ts","sourceRoot":"","sources":["../../../src/components/Link.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAEpC,wBAAgB,IAAI,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAC5C,KAAK,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAC5B,GAAG,CAAC,OAAO,CAoFb"}
1
+ {"version":3,"file":"Link.d.ts","sourceRoot":"","sources":["../../../src/components/Link.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAEpC,wBAAgB,IAAI,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAC5C,KAAK,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAC5B,GAAG,CAAC,OAAO,CAoFb"}
@@ -1,4 +1,4 @@
1
- import { Match, NotFound } from "./components";
1
+ import { Match, NotFound, Self } from "./components";
2
2
  import type { RouteViewProps } from "./types";
3
3
  import type { JSX } from "solid-js";
4
4
  declare function RouteViewRoot(props: Readonly<RouteViewProps>): JSX.Element;
@@ -7,7 +7,8 @@ declare namespace RouteViewRoot {
7
7
  }
8
8
  export declare const RouteView: typeof RouteViewRoot & {
9
9
  Match: typeof Match;
10
+ Self: typeof Self;
10
11
  NotFound: typeof NotFound;
11
12
  };
12
- export type { RouteViewProps, MatchProps as RouteViewMatchProps, NotFoundProps as RouteViewNotFoundProps, } from "./types";
13
+ export type { RouteViewProps, MatchProps as RouteViewMatchProps, SelfProps as RouteViewSelfProps, NotFoundProps as RouteViewNotFoundProps, } from "./types";
13
14
  //# sourceMappingURL=RouteView.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RouteView.d.ts","sourceRoot":"","sources":["../../../../src/components/RouteView/RouteView.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAK/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAEpC,iBAAS,aAAa,CAAC,KAAK,EAAE,QAAQ,CAAC,cAAc,CAAC,GAAG,GAAG,CAAC,OAAO,CAgCnE;kBAhCQ,aAAa;;;AAoCtB,eAAO,MAAM,SAAS;;;CAAoD,CAAC;AAE3E,YAAY,EACV,cAAc,EACd,UAAU,IAAI,mBAAmB,EACjC,aAAa,IAAI,sBAAsB,GACxC,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"RouteView.d.ts","sourceRoot":"","sources":["../../../../src/components/RouteView/RouteView.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAKrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAEpC,iBAAS,aAAa,CAAC,KAAK,EAAE,QAAQ,CAAC,cAAc,CAAC,GAAG,GAAG,CAAC,OAAO,CAgCnE;kBAhCQ,aAAa;;;AAoCtB,eAAO,MAAM,SAAS;;;;CAIpB,CAAC;AAEH,YAAY,EACV,cAAc,EACd,UAAU,IAAI,mBAAmB,EACjC,SAAS,IAAI,kBAAkB,EAC/B,aAAa,IAAI,sBAAsB,GACxC,MAAM,SAAS,CAAC"}
@@ -1,6 +1,7 @@
1
- import type { MatchProps, NotFoundProps } from "./types";
1
+ import type { MatchProps, NotFoundProps, SelfProps } from "./types";
2
2
  import type { JSX } from "solid-js";
3
3
  export declare const MATCH_MARKER: unique symbol;
4
+ export declare const SELF_MARKER: unique symbol;
4
5
  export declare const NOT_FOUND_MARKER: unique symbol;
5
6
  export interface MatchMarker {
6
7
  $$type: typeof MATCH_MARKER;
@@ -9,15 +10,24 @@ export interface MatchMarker {
9
10
  fallback?: JSX.Element;
10
11
  children: JSX.Element;
11
12
  }
13
+ export interface SelfMarker {
14
+ $$type: typeof SELF_MARKER;
15
+ fallback?: JSX.Element;
16
+ children: JSX.Element;
17
+ }
12
18
  export interface NotFoundMarker {
13
19
  $$type: typeof NOT_FOUND_MARKER;
14
20
  children: JSX.Element;
15
21
  }
16
- export type RouteViewMarker = MatchMarker | NotFoundMarker;
22
+ export type RouteViewMarker = MatchMarker | SelfMarker | NotFoundMarker;
17
23
  export declare function Match(props: MatchProps): JSX.Element;
18
24
  export declare namespace Match {
19
25
  var displayName: string;
20
26
  }
27
+ export declare function Self(props: SelfProps): JSX.Element;
28
+ export declare namespace Self {
29
+ var displayName: string;
30
+ }
21
31
  export declare function NotFound(props: NotFoundProps): JSX.Element;
22
32
  export declare namespace NotFound {
23
33
  var displayName: string;
@@ -1 +1 @@
1
- {"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../../../../src/components/RouteView/components.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACzD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAIpC,eAAO,MAAM,YAAY,eAA4B,CAAC;AAEtD,eAAO,MAAM,gBAAgB,eAA+B,CAAC;AAE7D,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,YAAY,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IACvB,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,OAAO,gBAAgB,CAAC;IAChC,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,cAAc,CAAC;AAE3D,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,GAAG,CAAC,OAAO,CAepD;yBAfe,KAAK;;;AAmBrB,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,GAAG,CAAC,OAAO,CAU1D;yBAVe,QAAQ"}
1
+ {"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../../../../src/components/RouteView/components.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAIpC,eAAO,MAAM,YAAY,eAA4B,CAAC;AAEtD,eAAO,MAAM,WAAW,eAA2B,CAAC;AAEpD,eAAO,MAAM,gBAAgB,eAA+B,CAAC;AAE7D,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,YAAY,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IACvB,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,OAAO,WAAW,CAAC;IAC3B,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IACvB,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,OAAO,gBAAgB,CAAC;IAChC,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,UAAU,GAAG,cAAc,CAAC;AAExE,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,GAAG,CAAC,OAAO,CAepD;yBAfe,KAAK;;;AAmBrB,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,GAAG,CAAC,OAAO,CAWlD;yBAXe,IAAI;;;AAepB,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,GAAG,CAAC,OAAO,CAU1D;yBAVe,QAAQ"}
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../../src/components/RouteView/helpers.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAGV,eAAe,EAChB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAEpC,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,MAAM,EACvB,KAAK,EAAE,OAAO,GACb,OAAO,CAMT;AAoBD,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,OAAO,EACjB,MAAM,EAAE,eAAe,EAAE,GACxB,IAAI,CAgBN;AAED,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,eAAe,EAAE,EAC3B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,GAAG,CAAC,OAAO,EAAE,CAyCf"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../../src/components/RouteView/helpers.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAGV,eAAe,EAEhB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAEpC,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,MAAM,EACvB,KAAK,EAAE,OAAO,GACb,OAAO,CAMT;AA6BD,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,OAAO,EACjB,MAAM,EAAE,eAAe,EAAE,GACxB,IAAI,CAoBN;AAqCD,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,eAAe,EAAE,EAC3B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,GAAG,CAAC,OAAO,EAAE,CAsCf"}
@@ -1,3 +1,3 @@
1
1
  export { RouteView } from "./RouteView";
2
- export type { RouteViewProps, RouteViewMatchProps, RouteViewNotFoundProps, } from "./RouteView";
2
+ export type { RouteViewProps, RouteViewMatchProps, RouteViewSelfProps, RouteViewNotFoundProps, } from "./RouteView";
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/RouteView/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,YAAY,EACV,cAAc,EACd,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/RouteView/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,YAAY,EACV,cAAc,EACd,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,aAAa,CAAC"}
@@ -9,6 +9,12 @@ export interface MatchProps {
9
9
  readonly fallback?: JSX.Element;
10
10
  readonly children: JSX.Element;
11
11
  }
12
+ export interface SelfProps {
13
+ /** Fallback content while children are suspended. */
14
+ readonly fallback?: JSX.Element;
15
+ /** Content to render when the active route name equals the parent RouteView's nodeName. */
16
+ readonly children: JSX.Element;
17
+ }
12
18
  export interface NotFoundProps {
13
19
  readonly children: JSX.Element;
14
20
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/components/RouteView/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAEpC,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC;CAChC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/components/RouteView/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAEpC,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,SAAS;IACxB,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IAChC,2FAA2F;IAC3F,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC;CAChC"}
@@ -1 +1 @@
1
- {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../../src/directives/link.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IAC7D,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,CAAC,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,wBAAgB,IAAI,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAC5C,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,MAAM,oBAAoB,CAAC,CAAC,CAAC,GACtC,IAAI,CA+DN"}
1
+ {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../../src/directives/link.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IAC7D,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,CAAC,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,wBAAgB,IAAI,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAC5C,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,MAAM,oBAAoB,CAAC,CAAC,CAAC,GACtC,IAAI,CA+DN"}
@@ -1,4 +1,6 @@
1
1
  export { createRouteAnnouncer } from "./route-announcer.js";
2
+ export { createScrollRestoration } from "./scroll-restore.js";
2
3
  export { shouldNavigate, buildHref, buildActiveClassName, shallowEqual, applyLinkA11y, } from "./link-utils.js";
3
4
  export type { RouteAnnouncerOptions } from "./route-announcer.js";
5
+ export type { ScrollRestorationOptions, ScrollRestorationMode, } from "./scroll-restore.js";
4
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dom-utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D,OAAO,EACL,cAAc,EACd,SAAS,EACT,oBAAoB,EACpB,YAAY,EACZ,aAAa,GACd,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dom-utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAE9D,OAAO,EACL,cAAc,EACd,SAAS,EACT,oBAAoB,EACpB,YAAY,EACZ,aAAa,GACd,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAElE,YAAY,EACV,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { Router } from "@real-router/core";
2
+ export type ScrollRestorationMode = "restore" | "top" | "manual";
3
+ export interface ScrollRestorationOptions {
4
+ mode?: ScrollRestorationMode | undefined;
5
+ anchorScrolling?: boolean | undefined;
6
+ scrollContainer?: (() => HTMLElement | null) | undefined;
7
+ }
8
+ export declare function createScrollRestoration(router: Router, options?: ScrollRestorationOptions): {
9
+ destroy: () => void;
10
+ };
11
+ //# sourceMappingURL=scroll-restore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-restore.d.ts","sourceRoot":"","sources":["../../../src/dom-utils/scroll-restore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAS,MAAM,mBAAmB,CAAC;AAUvD,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,KAAK,GAAG,QAAQ,CAAC;AAEjE,MAAM,WAAW,wBAAwB;IACvC,IAAI,CAAC,EAAE,qBAAqB,GAAG,SAAS,CAAC;IACzC,eAAe,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACtC,eAAe,CAAC,EAAE,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,SAAS,CAAC;CAC1D;AAOD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,wBAAwB,GACjC;IAAE,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CA+IzB"}
@@ -17,7 +17,7 @@ export { createStoreFromSource } from "./createStoreFromSource";
17
17
  export type { LinkProps, RouteState } from "./types";
18
18
  export type { RouterErrorBoundaryProps } from "./components/RouterErrorBoundary";
19
19
  export type { LinkDirectiveOptions } from "./directives/link";
20
- export type { RouteViewProps, RouteViewMatchProps, RouteViewNotFoundProps, } from "./components/RouteView";
20
+ export type { RouteViewProps, RouteViewMatchProps, RouteViewSelfProps, RouteViewNotFoundProps, } from "./components/RouteView";
21
21
  export type { Navigator } from "@real-router/core";
22
22
  export type { RouterTransitionSnapshot } from "@real-router/sources";
23
23
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEnD,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAEzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAEvE,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAEzC,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAElE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAExD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErD,YAAY,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAEjF,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAE9D,YAAY,EACV,cAAc,EACd,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAEhC,YAAY,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,YAAY,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEnD,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAEzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAEvE,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAEzC,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAElE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAExD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErD,YAAY,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAEjF,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAE9D,YAAY,EACV,cAAc,EACd,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAEhC,YAAY,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,YAAY,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@real-router/solid",
3
- "version": "0.5.2",
3
+ "version": "0.7.0",
4
4
  "type": "commonjs",
5
5
  "description": "Solid.js integration for Real-Router",
6
6
  "main": "./dist/cjs/index.js",
@@ -51,7 +51,7 @@
51
51
  "license": "MIT",
52
52
  "sideEffects": false,
53
53
  "dependencies": {
54
- "@real-router/core": "^0.50.0",
54
+ "@real-router/core": "^0.50.1",
55
55
  "@real-router/route-utils": "^0.2.1",
56
56
  "@real-router/sources": "^0.7.2"
57
57
  },
@@ -71,7 +71,7 @@
71
71
  "solid-js": "1.9.12",
72
72
  "vite-plugin-solid": "2.11.11",
73
73
  "vitest": "4.1.0",
74
- "@real-router/browser-plugin": "^0.14.0"
74
+ "@real-router/browser-plugin": "^0.15.1"
75
75
  },
76
76
  "peerDependencies": {
77
77
  "solid-js": ">=1.7.0"
@@ -4,14 +4,16 @@ import { createSelector, onCleanup, onMount } from "solid-js";
4
4
 
5
5
  import { RouterContext, RouteContext } from "./context";
6
6
  import { createSignalFromSource } from "./createSignalFromSource";
7
- import { createRouteAnnouncer } from "./dom-utils/index.js";
7
+ import { createRouteAnnouncer, createScrollRestoration } from "./dom-utils";
8
8
 
9
+ import type { ScrollRestorationOptions } from "./dom-utils";
9
10
  import type { Router } from "@real-router/core";
10
11
  import type { ParentProps, JSX } from "solid-js";
11
12
 
12
13
  export interface RouteProviderProps {
13
14
  router: Router;
14
15
  announceNavigation?: boolean;
16
+ scrollRestoration?: ScrollRestorationOptions;
15
17
  }
16
18
 
17
19
  export function isRouteActive(
@@ -39,6 +41,18 @@ export function RouterProvider(
39
41
  });
40
42
  });
41
43
 
44
+ onMount(() => {
45
+ if (!props.scrollRestoration) {
46
+ return;
47
+ }
48
+
49
+ const sr = createScrollRestoration(props.router, props.scrollRestoration);
50
+
51
+ onCleanup(() => {
52
+ sr.destroy();
53
+ });
54
+ });
55
+
42
56
  const navigator = getNavigator(props.router);
43
57
  const routeSource = createRouteSource(props.router);
44
58
  const routeSignal = createSignalFromSource(routeSource);
@@ -4,11 +4,7 @@ import { createMemo, mergeProps, splitProps, useContext } from "solid-js";
4
4
  import { EMPTY_PARAMS, EMPTY_OPTIONS } from "../constants";
5
5
  import { RouterContext } from "../context";
6
6
  import { createSignalFromSource } from "../createSignalFromSource";
7
- import {
8
- shouldNavigate,
9
- buildHref,
10
- buildActiveClassName,
11
- } from "../dom-utils/index.js";
7
+ import { shouldNavigate, buildHref, buildActiveClassName } from "../dom-utils";
12
8
 
13
9
  import type { LinkProps } from "../types";
14
10
  import type { Params } from "@real-router/core";
@@ -1,6 +1,6 @@
1
1
  import { children as resolveChildren, createMemo } from "solid-js";
2
2
 
3
- import { Match, NotFound } from "./components";
3
+ import { Match, NotFound, Self } from "./components";
4
4
  import { buildRenderList, collectElements } from "./helpers";
5
5
  import { useRouteNode } from "../../hooks/useRouteNode";
6
6
 
@@ -44,10 +44,15 @@ function RouteViewRoot(props: Readonly<RouteViewProps>): JSX.Element {
44
44
 
45
45
  RouteViewRoot.displayName = "RouteView";
46
46
 
47
- export const RouteView = Object.assign(RouteViewRoot, { Match, NotFound });
47
+ export const RouteView = Object.assign(RouteViewRoot, {
48
+ Match,
49
+ Self,
50
+ NotFound,
51
+ });
48
52
 
49
53
  export type {
50
54
  RouteViewProps,
51
55
  MatchProps as RouteViewMatchProps,
56
+ SelfProps as RouteViewSelfProps,
52
57
  NotFoundProps as RouteViewNotFoundProps,
53
58
  } from "./types";
@@ -1,10 +1,12 @@
1
- import type { MatchProps, NotFoundProps } from "./types";
1
+ import type { MatchProps, NotFoundProps, SelfProps } from "./types";
2
2
  import type { JSX } from "solid-js";
3
3
 
4
4
  // Local (non-global) Symbols — Symbol.for() would expose markers to spoofing
5
5
  // via the global Symbol registry. See Gotchas section "RouteView Marker Objects".
6
6
  export const MATCH_MARKER = Symbol("RouteView.Match");
7
7
 
8
+ export const SELF_MARKER = Symbol("RouteView.Self");
9
+
8
10
  export const NOT_FOUND_MARKER = Symbol("RouteView.NotFound");
9
11
 
10
12
  export interface MatchMarker {
@@ -15,12 +17,18 @@ export interface MatchMarker {
15
17
  children: JSX.Element;
16
18
  }
17
19
 
20
+ export interface SelfMarker {
21
+ $$type: typeof SELF_MARKER;
22
+ fallback?: JSX.Element;
23
+ children: JSX.Element;
24
+ }
25
+
18
26
  export interface NotFoundMarker {
19
27
  $$type: typeof NOT_FOUND_MARKER;
20
28
  children: JSX.Element;
21
29
  }
22
30
 
23
- export type RouteViewMarker = MatchMarker | NotFoundMarker;
31
+ export type RouteViewMarker = MatchMarker | SelfMarker | NotFoundMarker;
24
32
 
25
33
  export function Match(props: MatchProps): JSX.Element {
26
34
  const result: MatchMarker = {
@@ -41,6 +49,21 @@ export function Match(props: MatchProps): JSX.Element {
41
49
 
42
50
  Match.displayName = "RouteView.Match";
43
51
 
52
+ export function Self(props: SelfProps): JSX.Element {
53
+ const result: SelfMarker = {
54
+ $$type: SELF_MARKER,
55
+ fallback: props.fallback,
56
+ get children(): JSX.Element {
57
+ return props.children;
58
+ },
59
+ };
60
+
61
+ // See Match for the marker-pattern rationale.
62
+ return result as unknown as JSX.Element;
63
+ }
64
+
65
+ Self.displayName = "RouteView.Self";
66
+
44
67
  export function NotFound(props: NotFoundProps): JSX.Element {
45
68
  const result: NotFoundMarker = {
46
69
  $$type: NOT_FOUND_MARKER,