@blueshed/railroad 0.3.0 → 0.3.2
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/jsx.ts +10 -0
- package/package.json +1 -1
- package/routes.ts +25 -5
package/jsx.ts
CHANGED
|
@@ -327,6 +327,16 @@ export function list<T>(
|
|
|
327
327
|
// Existing keyed item — push new value into its signal
|
|
328
328
|
entry.item!.set(arr[i]!);
|
|
329
329
|
entry.index!.set(i);
|
|
330
|
+
} else {
|
|
331
|
+
// Index-based — dispose old, recreate with new item
|
|
332
|
+
const oldNode = entry.node;
|
|
333
|
+
entry.dispose();
|
|
334
|
+
pushDisposeScope();
|
|
335
|
+
const node = (keyFnOrRender as (item: T, index: number) => Node)(arr[i]!, i);
|
|
336
|
+
const dispose = popDisposeScope();
|
|
337
|
+
entry = { node, dispose };
|
|
338
|
+
entries.set(key, entry);
|
|
339
|
+
if (oldNode.parentNode) oldNode.parentNode.replaceChild(node, oldNode);
|
|
330
340
|
}
|
|
331
341
|
|
|
332
342
|
// Move or insert into correct position
|
package/package.json
CHANGED
package/routes.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* navigate(path) — set location.hash programmatically
|
|
8
8
|
* matchRoute(pattern, path) — pure pattern matcher, returns params or null
|
|
9
9
|
*
|
|
10
|
-
* Handlers receive (params, params$) and return a Node.
|
|
10
|
+
* Handlers receive (params, params$) and return a Node (sync or async).
|
|
11
11
|
* params — plain object for destructuring: ({ id }) => ...
|
|
12
12
|
* params$ — Signal that updates when params change within the same pattern
|
|
13
13
|
*
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* routes(app, {
|
|
17
17
|
* "/": () => <Home />,
|
|
18
18
|
* "/site/:id": ({ id }, params$) => <SiteDetail id={id} params$={params$} />,
|
|
19
|
+
* "/status": async () => { const s = await api.get(); return <Status data={s} />; },
|
|
19
20
|
* });
|
|
20
21
|
*/
|
|
21
22
|
|
|
@@ -81,7 +82,7 @@ export function navigate(path: string): void {
|
|
|
81
82
|
type RouteHandler = (
|
|
82
83
|
params: Record<string, string>,
|
|
83
84
|
params$: Signal<Record<string, string>>,
|
|
84
|
-
) => Node
|
|
85
|
+
) => Node | Promise<Node>;
|
|
85
86
|
|
|
86
87
|
export function routes(
|
|
87
88
|
target: HTMLElement,
|
|
@@ -91,8 +92,15 @@ export function routes(
|
|
|
91
92
|
let activePattern: string | null = null;
|
|
92
93
|
let activeParams: Signal<Record<string, string>> | null = null;
|
|
93
94
|
let activeDispose: Dispose | null = null;
|
|
95
|
+
let runId = 0;
|
|
96
|
+
let asyncPending = false;
|
|
94
97
|
|
|
95
98
|
function teardown() {
|
|
99
|
+
runId++;
|
|
100
|
+
if (asyncPending) {
|
|
101
|
+
popDisposeScope()();
|
|
102
|
+
asyncPending = false;
|
|
103
|
+
}
|
|
96
104
|
if (activeDispose) activeDispose();
|
|
97
105
|
activeDispose = null;
|
|
98
106
|
activePattern = null;
|
|
@@ -101,11 +109,23 @@ export function routes(
|
|
|
101
109
|
}
|
|
102
110
|
|
|
103
111
|
function run(handler: RouteHandler, params: Record<string, string>) {
|
|
112
|
+
const myRunId = ++runId;
|
|
104
113
|
activeParams = signal(params);
|
|
105
114
|
pushDisposeScope();
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
115
|
+
const result = handler(params, activeParams);
|
|
116
|
+
|
|
117
|
+
if (result instanceof Promise) {
|
|
118
|
+
asyncPending = true;
|
|
119
|
+
result.then((node) => {
|
|
120
|
+
if (myRunId !== runId) return; // navigated away during await
|
|
121
|
+
asyncPending = false;
|
|
122
|
+
activeDispose = popDisposeScope();
|
|
123
|
+
target.appendChild(node);
|
|
124
|
+
});
|
|
125
|
+
} else {
|
|
126
|
+
activeDispose = popDisposeScope();
|
|
127
|
+
target.appendChild(result);
|
|
128
|
+
}
|
|
109
129
|
}
|
|
110
130
|
|
|
111
131
|
const disposeEffect = effect(() => {
|