@tschk/moonshine-router 0.3.5 → 0.3.7
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/package.json +3 -3
- package/src/navigation.ts +27 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tschk/moonshine-router",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
4
4
|
"description": "Renderer-neutral route graph and matcher with optional React navigation",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"type": "module",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"test": "bun test"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@tschk/moonshine": "^0.3.
|
|
20
|
-
"@tschk/moonshine-framework": "^0.3.
|
|
19
|
+
"@tschk/moonshine": "^0.3.6",
|
|
20
|
+
"@tschk/moonshine-framework": "^0.3.6"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/bun": "1.3.14",
|
package/src/navigation.ts
CHANGED
|
@@ -15,17 +15,36 @@ import { createRouteGraph, matchRoutes, type RouteMatch } from "./graph.js";
|
|
|
15
15
|
|
|
16
16
|
export type RouteParams = Record<string, string>;
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* The location the signal carries: pathname plus query.
|
|
20
|
+
*
|
|
21
|
+
* The query has to be in the signal, not read off `window.location` at render
|
|
22
|
+
* time. A navigation that changes only the query would otherwise write the
|
|
23
|
+
* same pathname back, notify no subscriber, and leave every `useSearchParams`
|
|
24
|
+
* caller rendering the previous query — a tab bar driven by `?section=` would
|
|
25
|
+
* change the URL and not the page. The hash stays out: it is still pushed to
|
|
26
|
+
* history, but these hooks do not read it and including it would corrupt the
|
|
27
|
+
* query when consumers split on the first "?".
|
|
28
|
+
*/
|
|
18
29
|
function getBrowserPath(): string {
|
|
19
30
|
if (typeof window === "undefined") return "/";
|
|
20
|
-
return window.location.pathname || "/";
|
|
31
|
+
return (window.location.pathname || "/") + window.location.search;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Splits `"/a/b?x=1"` into its pathname and its raw query string. */
|
|
35
|
+
export function splitLocation(location: string): [string, string] {
|
|
36
|
+
const index = location.indexOf("?");
|
|
37
|
+
return index === -1
|
|
38
|
+
? [location, ""]
|
|
39
|
+
: [location.slice(0, index), location.slice(index + 1)];
|
|
21
40
|
}
|
|
22
41
|
|
|
23
42
|
export type MoonshineRouterInstance = {
|
|
24
43
|
/** Navigate via History API (no full reload). */
|
|
25
44
|
navigate: (to: string, options?: { replace?: boolean }) => void;
|
|
26
|
-
/** Current pathname (reactive signal). */
|
|
45
|
+
/** Current pathname and query (reactive signal). */
|
|
27
46
|
getLocation: () => string;
|
|
28
|
-
/** Subscribe-friendly location signal. */
|
|
47
|
+
/** Subscribe-friendly location signal, carrying pathname and query. */
|
|
29
48
|
location: Signal<string>;
|
|
30
49
|
/** Sync signal from `window.location`. */
|
|
31
50
|
syncFromBrowser: () => void;
|
|
@@ -52,7 +71,7 @@ export function createMoonshineRouter(
|
|
|
52
71
|
} else {
|
|
53
72
|
window.history.pushState({}, "", url.pathname + url.search + url.hash);
|
|
54
73
|
}
|
|
55
|
-
location.set(url.pathname);
|
|
74
|
+
location.set(url.pathname + url.search);
|
|
56
75
|
};
|
|
57
76
|
|
|
58
77
|
return {
|
|
@@ -77,7 +96,7 @@ export function navigate(to: string, options?: { replace?: boolean }): void {
|
|
|
77
96
|
currentRouter().navigate(to, options);
|
|
78
97
|
}
|
|
79
98
|
|
|
80
|
-
/** Current pathname (reactive; active or fallback router). */
|
|
99
|
+
/** Current pathname and query (reactive; active or fallback router). */
|
|
81
100
|
export function getLocation(): string {
|
|
82
101
|
return currentRouter().getLocation();
|
|
83
102
|
}
|
|
@@ -158,7 +177,9 @@ export function MoonshineRouter(props: MoonshineRouterProps): ReactNode {
|
|
|
158
177
|
() => runtime.location.peek(),
|
|
159
178
|
() => props.path ?? "/",
|
|
160
179
|
);
|
|
161
|
-
|
|
180
|
+
// Routes are matched on the pathname alone; the query rides along in the
|
|
181
|
+
// signal only so that changing it re-renders.
|
|
182
|
+
const pathname = splitLocation(props.path ?? browserPath)[0];
|
|
162
183
|
const [ready, setReady] = useState(
|
|
163
184
|
typeof window === "undefined" || props.path !== undefined,
|
|
164
185
|
);
|