@rangojs/router 0.0.0-experimental.42 → 0.0.0-experimental.4518794d
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/dist/vite/index.js
CHANGED
|
@@ -1745,7 +1745,7 @@ import { resolve } from "node:path";
|
|
|
1745
1745
|
// package.json
|
|
1746
1746
|
var package_default = {
|
|
1747
1747
|
name: "@rangojs/router",
|
|
1748
|
-
version: "0.0.0-experimental.
|
|
1748
|
+
version: "0.0.0-experimental.4518794d",
|
|
1749
1749
|
description: "Django-inspired RSC router with composable URL patterns",
|
|
1750
1750
|
keywords: [
|
|
1751
1751
|
"react",
|
package/package.json
CHANGED
|
@@ -461,6 +461,10 @@ export function createNavigationBridge(
|
|
|
461
461
|
}
|
|
462
462
|
eventController.setParams(cachedParams);
|
|
463
463
|
|
|
464
|
+
// Scroll to top immediately to avoid flicker from the previous
|
|
465
|
+
// page's scroll position while React paints the cached content.
|
|
466
|
+
window.scrollTo(0, 0);
|
|
467
|
+
|
|
464
468
|
const popstateUpdate = {
|
|
465
469
|
root,
|
|
466
470
|
metadata: {
|
|
@@ -485,16 +489,8 @@ export function createNavigationBridge(
|
|
|
485
489
|
onUpdate(popstateUpdate);
|
|
486
490
|
}
|
|
487
491
|
|
|
488
|
-
// Restore scroll position
|
|
489
|
-
|
|
490
|
-
// the DOM before we measure scrollHeight and restore scroll position.
|
|
491
|
-
const defer =
|
|
492
|
-
typeof requestAnimationFrame === "function"
|
|
493
|
-
? requestAnimationFrame
|
|
494
|
-
: (fn: () => void) => setTimeout(fn, 0);
|
|
495
|
-
defer(() => {
|
|
496
|
-
handleNavigationEnd({ restore: true, isStreaming });
|
|
497
|
-
});
|
|
492
|
+
// Restore the actual saved scroll position after React paints.
|
|
493
|
+
handleNavigationEnd({ restore: true, isStreaming });
|
|
498
494
|
|
|
499
495
|
// SWR: If stale, trigger background revalidation
|
|
500
496
|
if (isStale) {
|
|
@@ -7,7 +7,6 @@ import type {
|
|
|
7
7
|
import { generateHistoryKey } from "./navigation-store.js";
|
|
8
8
|
import {
|
|
9
9
|
handleNavigationStart,
|
|
10
|
-
handleNavigationEnd,
|
|
11
10
|
ensureHistoryKey,
|
|
12
11
|
} from "./scroll-restoration.js";
|
|
13
12
|
import type { EventController, NavigationHandle } from "./event-controller.js";
|
|
@@ -81,11 +80,12 @@ export interface BoundTransaction {
|
|
|
81
80
|
readonly currentUrl: string;
|
|
82
81
|
/** Start streaming and get a token to end it when the stream completes */
|
|
83
82
|
startStreaming(): StreamingToken;
|
|
83
|
+
/** Commit the navigation. Returns the effective scroll option for the caller to handle. */
|
|
84
84
|
commit(
|
|
85
85
|
segmentIds: string[],
|
|
86
86
|
segments: ResolvedSegment[],
|
|
87
87
|
overrides?: BoundCommitOverrides,
|
|
88
|
-
):
|
|
88
|
+
): { scroll?: boolean };
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/**
|
|
@@ -93,7 +93,7 @@ export interface BoundTransaction {
|
|
|
93
93
|
* Uses the event controller handle for lifecycle management
|
|
94
94
|
*/
|
|
95
95
|
interface NavigationTransaction extends Disposable {
|
|
96
|
-
commit(options: CommitOptions):
|
|
96
|
+
commit(options: CommitOptions): { scroll?: boolean };
|
|
97
97
|
with(
|
|
98
98
|
options: Omit<CommitOptions, "segmentIds" | "segments">,
|
|
99
99
|
): BoundTransaction;
|
|
@@ -120,7 +120,7 @@ export function createNavigationTransaction(
|
|
|
120
120
|
/**
|
|
121
121
|
* Commit the navigation - updates store and URL atomically
|
|
122
122
|
*/
|
|
123
|
-
function commit(opts: CommitOptions):
|
|
123
|
+
function commit(opts: CommitOptions): { scroll?: boolean } {
|
|
124
124
|
committed = true;
|
|
125
125
|
|
|
126
126
|
const {
|
|
@@ -150,7 +150,7 @@ export function createNavigationTransaction(
|
|
|
150
150
|
// Without this, the entry lingers and weakens state-machine invariants.
|
|
151
151
|
handle.complete(parsedUrl);
|
|
152
152
|
debugLog("[Browser] Cache-only commit, historyKey:", historyKey);
|
|
153
|
-
return;
|
|
153
|
+
return { scroll: false };
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
// Save current scroll position before navigating
|
|
@@ -172,7 +172,7 @@ export function createNavigationTransaction(
|
|
|
172
172
|
debugLog("[Browser] Store updated (action)");
|
|
173
173
|
// Complete navigation to clear loading state
|
|
174
174
|
handle.complete(parsedUrl);
|
|
175
|
-
return;
|
|
175
|
+
return { scroll: false };
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
// Build history state - include user state, intercept info, and server-set state
|
|
@@ -205,14 +205,16 @@ export function createNavigationTransaction(
|
|
|
205
205
|
// Complete the navigation in event controller (sets idle state, updates location)
|
|
206
206
|
handle.complete(parsedUrl);
|
|
207
207
|
|
|
208
|
-
//
|
|
209
|
-
|
|
208
|
+
// NOTE: Scroll is NOT handled here. The caller (partial-update.ts) handles
|
|
209
|
+
// scroll AFTER onUpdate() so React has the new content before we scroll.
|
|
210
210
|
|
|
211
211
|
debugLog(
|
|
212
212
|
"[Browser] Navigation committed, historyKey:",
|
|
213
213
|
historyKey,
|
|
214
214
|
intercept ? "(intercept)" : "",
|
|
215
215
|
);
|
|
216
|
+
|
|
217
|
+
return { scroll };
|
|
216
218
|
}
|
|
217
219
|
|
|
218
220
|
return {
|
|
@@ -263,7 +265,7 @@ export function createNavigationTransaction(
|
|
|
263
265
|
overrides?.state !== undefined ? overrides.state : opts.state;
|
|
264
266
|
// Server-set location state: only from overrides (set by partial-update)
|
|
265
267
|
const serverState = overrides?.serverState;
|
|
266
|
-
commit({
|
|
268
|
+
return commit({
|
|
267
269
|
...opts,
|
|
268
270
|
segmentIds,
|
|
269
271
|
segments,
|
|
@@ -19,6 +19,7 @@ import type { BoundTransaction } from "./navigation-transaction.js";
|
|
|
19
19
|
import { ServerRedirect } from "../errors.js";
|
|
20
20
|
import { debugLog } from "./logging.js";
|
|
21
21
|
import { validateRedirectOrigin } from "./validate-redirect-origin.js";
|
|
22
|
+
import { handleNavigationEnd } from "./scroll-restoration.js";
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* Configuration for creating a partial updater
|
|
@@ -246,7 +247,10 @@ export function createPartialUpdater(
|
|
|
246
247
|
forceAwait: true,
|
|
247
248
|
});
|
|
248
249
|
|
|
249
|
-
tx.commit(
|
|
250
|
+
const { scroll: commitScroll } = tx.commit(
|
|
251
|
+
matchedIds,
|
|
252
|
+
existingSegments,
|
|
253
|
+
);
|
|
250
254
|
|
|
251
255
|
// Include cachedHandleData in metadata so NavigationProvider can restore
|
|
252
256
|
// breadcrumbs and other handle data from cache.
|
|
@@ -276,6 +280,7 @@ export function createPartialUpdater(
|
|
|
276
280
|
onUpdate(cachedUpdate);
|
|
277
281
|
}
|
|
278
282
|
|
|
283
|
+
handleNavigationEnd({ scroll: commitScroll });
|
|
279
284
|
debugLog("[Browser] Navigation complete (rendered from cache)");
|
|
280
285
|
return;
|
|
281
286
|
}
|
|
@@ -290,13 +295,17 @@ export function createPartialUpdater(
|
|
|
290
295
|
forceAwait: true,
|
|
291
296
|
});
|
|
292
297
|
|
|
293
|
-
tx.commit(
|
|
298
|
+
const { scroll: leaveScroll } = tx.commit(
|
|
299
|
+
matchedIds,
|
|
300
|
+
existingSegments,
|
|
301
|
+
);
|
|
294
302
|
|
|
295
303
|
onUpdate({
|
|
296
304
|
root: newTree,
|
|
297
305
|
metadata: payload.metadata,
|
|
298
306
|
});
|
|
299
307
|
|
|
308
|
+
handleNavigationEnd({ scroll: leaveScroll });
|
|
300
309
|
debugLog("[Browser] Navigation complete (left intercept)");
|
|
301
310
|
return;
|
|
302
311
|
}
|
|
@@ -426,7 +435,11 @@ export function createPartialUpdater(
|
|
|
426
435
|
: serverLocationState
|
|
427
436
|
? { serverState: serverLocationState }
|
|
428
437
|
: undefined;
|
|
429
|
-
tx.commit(
|
|
438
|
+
const { scroll: navScroll } = tx.commit(
|
|
439
|
+
allSegmentIds,
|
|
440
|
+
reconciled.segments,
|
|
441
|
+
overrides,
|
|
442
|
+
);
|
|
430
443
|
|
|
431
444
|
// For stale revalidation: verify history key hasn't changed before updating UI
|
|
432
445
|
if (mode.type === "stale-revalidation") {
|
|
@@ -471,6 +484,9 @@ export function createPartialUpdater(
|
|
|
471
484
|
});
|
|
472
485
|
}
|
|
473
486
|
|
|
487
|
+
// Scroll after onUpdate so React has the new content before we scroll
|
|
488
|
+
handleNavigationEnd({ scroll: navScroll });
|
|
489
|
+
|
|
474
490
|
debugLog("[Browser] Navigation complete");
|
|
475
491
|
return;
|
|
476
492
|
} else {
|
|
@@ -494,11 +510,11 @@ export function createPartialUpdater(
|
|
|
494
510
|
}
|
|
495
511
|
|
|
496
512
|
const fullUpdateServerState = payload.metadata?.locationState;
|
|
497
|
-
|
|
498
|
-
tx.commit(segmentIds, segments, {
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
513
|
+
const { scroll: fullScroll } = fullUpdateServerState
|
|
514
|
+
? tx.commit(segmentIds, segments, {
|
|
515
|
+
serverState: fullUpdateServerState,
|
|
516
|
+
})
|
|
517
|
+
: tx.commit(segmentIds, segments);
|
|
502
518
|
|
|
503
519
|
const fullHasTransition = segments.some(
|
|
504
520
|
(s: ResolvedSegment) => s.transition,
|
|
@@ -542,6 +558,7 @@ export function createPartialUpdater(
|
|
|
542
558
|
});
|
|
543
559
|
}
|
|
544
560
|
|
|
561
|
+
handleNavigationEnd({ scroll: fullScroll });
|
|
545
562
|
return;
|
|
546
563
|
}
|
|
547
564
|
}
|
|
@@ -370,13 +370,21 @@ export function handleNavigationEnd(options: {
|
|
|
370
370
|
// Fall through to hash or top if no saved position
|
|
371
371
|
}
|
|
372
372
|
|
|
373
|
-
//
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
373
|
+
// Defer hash and scroll-to-top to after React paints the new content,
|
|
374
|
+
// so the user doesn't see the current page jump before the new route appears.
|
|
375
|
+
const defer =
|
|
376
|
+
typeof requestAnimationFrame === "function"
|
|
377
|
+
? requestAnimationFrame
|
|
378
|
+
: (fn: () => void) => setTimeout(fn, 0);
|
|
379
|
+
defer(() => {
|
|
380
|
+
// Try hash scrolling first
|
|
381
|
+
if (scrollToHash()) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
377
384
|
|
|
378
|
-
|
|
379
|
-
|
|
385
|
+
// Default: scroll to top
|
|
386
|
+
scrollToTop();
|
|
387
|
+
});
|
|
380
388
|
}
|
|
381
389
|
|
|
382
390
|
/**
|